[gnome-keyring/gck-work] [gck] In preparation for public release, rename library.



commit 8d80ac517dea7f35b5bb7b72fe425681d4ed5601
Author: Stef Walter <stef memberwebs com>
Date:   Thu Jul 29 16:34:07 2010 +0200

    [gck] In preparation for public release, rename library.
    
     * Next steps will include cleaning up the API making it ready
       for gobject introspection etc..

 Makefile.am                     |    1 +
 configure.in                    |   11 +
 gck/.gitignore                  |    2 +
 gck/Makefile.am                 |   66 +
 gck/gck-attributes.c            | 1428 +++++++++++++++++++
 gck/gck-call.c                  |  541 ++++++++
 gck/gck-marshal.list            |    3 +
 gck/gck-misc.c                  |  437 ++++++
 gck/gck-module.c                | 1228 +++++++++++++++++
 gck/gck-object.c                | 1615 ++++++++++++++++++++++
 gck/gck-private.h               |  162 +++
 gck/gck-session.c               | 2886 +++++++++++++++++++++++++++++++++++++++
 gck/gck-slot.c                  | 1061 ++++++++++++++
 gck/gck.h                       | 1297 ++++++++++++++++++
 gck/gck.pc.in                   |   14 +
 gck/pkcs11.h                    |   30 +
 gck/tests/Makefile.am           |   42 +
 gck/tests/gck-test-module.c     | 1702 +++++++++++++++++++++++
 gck/tests/gck-test.h            |   46 +
 gck/tests/test-gck-attributes.c |  528 +++++++
 gck/tests/test-gck-crypto.c     |  595 ++++++++
 gck/tests/test-gck-mechanism.c  |   60 +
 gck/tests/test-gck-module.c     |  159 +++
 gck/tests/test-gck-object.c     |  463 +++++++
 gck/tests/test-gck-session.c    |  323 +++++
 gck/tests/test-gck-slot.c       |  147 ++
 26 files changed, 14847 insertions(+), 0 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index ca70b91..88c56ce 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,6 +12,7 @@ endif
 
 SUBDIRS = \
 	. \
+	gck \
 	gp11 \
 	egg \
 	gcr \
diff --git a/configure.in b/configure.in
index e3a14ca..74739cd 100644
--- a/configure.in
+++ b/configure.in
@@ -11,6 +11,10 @@ GP11_MAJOR=0		# Increment for major version number, breaks old apps.
 GP11_REVISION=0		# Increment for internal changes, nothing affected.
 GP11_AGE=0		# Increment for interface that doesn't break anything
 
+GCK_MAJOR=0		# Increment for major version number, breaks old apps.
+GCK_REVISION=0		# Increment for internal changes, nothing affected.
+GCK_AGE=0		# Increment for interface that doesn't break anything
+
 GCR_MAJOR=0		# Increment for major version number, breaks old apps.
 GCR_REVISION=0		# Increment for internal changes, nothing affected.
 GCR_AGE=0		# Increment for interface that doesn't break anything
@@ -577,6 +581,10 @@ GP11_LT_RELEASE=$GP11_MAJOR:$GP11_REVISION:$GP11_AGE
 AC_SUBST(GP11_LT_RELEASE)
 AC_SUBST(GP11_MAJOR)
 
+GCK_LT_RELEASE=$GCK_MAJOR:$GCK_REVISION:$GCK_AGE
+AC_SUBST(GCK_LT_RELEASE)
+AC_SUBST(GCK_MAJOR)
+
 GCR_LT_RELEASE=$GCR_MAJOR:$GCR_REVISION:$GCR_AGE
 AC_SUBST(GCR_LT_RELEASE)
 AC_SUBST(GCR_MAJOR)
@@ -624,6 +632,9 @@ docs/reference/gcr/Makefile
 docs/reference/gp11/Makefile
 egg/Makefile
 egg/tests/Makefile
+gck/gck.pc
+gck/Makefile
+gck/tests/Makefile
 gcr/gcr.pc
 gcr/Makefile
 gcr/tests/Makefile
diff --git a/gck/.gitignore b/gck/.gitignore
new file mode 100644
index 0000000..4fbdec3
--- /dev/null
+++ b/gck/.gitignore
@@ -0,0 +1,2 @@
+/gck-*.pc
+/gck.pc
diff --git a/gck/Makefile.am b/gck/Makefile.am
new file mode 100644
index 0000000..0173680
--- /dev/null
+++ b/gck/Makefile.am
@@ -0,0 +1,66 @@
+incdir = $(includedir)/gck
+
+inc_HEADERS = \
+	gck.h
+
+INCLUDES = \
+	-I$(top_builddir) \
+	-I$(top_srcdir) \
+	$(GOBJECT_CFLAGS) \
+	$(GTHREAD_CFLAGS) \
+	$(GLIB_CFLAGS)
+
+BUILT_SOURCES = \
+	gck-marshal.c gck-marshal.h
+
+lib_LTLIBRARIES = libgck.la
+
+libgck_la_SOURCES = \
+	gck.h gck-private.h pkcs11.h \
+	gck-attributes.c \
+	gck-call.c \
+	gck-misc.c \
+	gck-module.c \
+	gck-object.c \
+	gck-session.c \
+	gck-slot.c \
+	$(BUILT_SOURCES)
+
+libgck_la_LDFLAGS = \
+	-version-info $(GCK_LT_RELEASE) \
+	-no-undefined -export-symbols-regex 'gck_*'
+
+libgck_la_LIBADD = \
+	$(GOBJECT_LIBS) \
+	$(GTHREAD_LIBS) \
+	$(GIO_LIBS) \
+	$(GLIB_LIBS)
+
+gck-marshal.h: gck-marshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --header --prefix=_gck_marshal > $@
+
+gck-marshal.c: gck-marshal.list $(GLIB_GENMARSHAL)
+	echo "#include \"gck-marshal.h\"" > $@ && \
+	$(GLIB_GENMARSHAL) $< --body --prefix=_gck_marshal >> $@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = gck-$(GCK_MAJOR).pc
+
+EXTRA_DIST = \
+	gck.pc.in \
+	gck-marshal.list
+
+DISTCLEANFILES = \
+	gck-$(GCK_MAJOR).pc
+
+gck-$(GCK_MAJOR).pc: gck.pc
+	cp gck.pc gck-$(GCK_MAJOR).pc
+
+if WITH_TESTS
+TESTS_DIR = tests
+else
+TESTS_DIR =
+endif
+
+SUBDIRS = . \
+	$(TESTS_DIR)
diff --git a/gck/gck-attributes.c b/gck/gck-attributes.c
new file mode 100644
index 0000000..30250e8
--- /dev/null
+++ b/gck/gck-attributes.c
@@ -0,0 +1,1428 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-attribute.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * SECTION:gck-attribute
+ * @title: GckAttribute
+ * @short_description: A PKCS11 attribute.
+ *
+ * This structure represents a PKCS11 CK_ATTRIBUTE. These attributes contain information
+ * about a PKCS11 object. Use gck_object_get() or gck_object_set() to set and retrieve
+ * attributes on an object.
+ */
+
+/**
+ * GckAttribute:
+ * @type: The attribute type, such as CKA_LABEL.
+ * @value: The value of the attribute. May be NULL.
+ * @length: The length of the attribute. May be G_MAXULONG if the attribute is invalid.
+ *
+ * This structure represents a PKCS11 CK_ATTRIBUTE.
+ */
+
+static void
+attribute_init (GckAttribute *attr, gulong attr_type,
+                gconstpointer value, gsize length,
+                GckAllocator allocator)
+{
+	g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE));
+	g_assert (allocator);
+
+	memset (attr, 0, sizeof (GckAttribute));
+	attr->type = attr_type;
+	attr->length = length;
+	if (value) {
+		attr->value = (allocator) (NULL, length ? length : 1);
+		g_assert (attr->value);
+		memcpy (attr->value, value, length);
+	}
+}
+
+/**
+ * gck_attribute_init:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The raw value of the attribute.
+ * @length: The length of the raw value.
+ *
+ * Initialize a PKCS#11 attribute. This copies the value memory
+ * into an internal buffer.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init (GckAttribute *attr, gulong attr_type,
+                    gconstpointer value, gsize length)
+{
+	g_return_if_fail (attr);
+	attribute_init (attr, attr_type, value, length, g_realloc);
+}
+
+/**
+ * gck_attribute_init_invalid:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ *
+ * Initialize a PKCS#11 attribute to an 'invalid' or 'not found'
+ * state. Specifically this sets the value length to (CK_ULONG)-1
+ * as specified in the PKCS#11 specification.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init_invalid (GckAttribute *attr, gulong attr_type)
+{
+	g_return_if_fail (attr);
+	g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE));
+	memset (attr, 0, sizeof (GckAttribute));
+	attr->type = attr_type;
+	attr->length = (gulong)-1;
+}
+
+/**
+ * gck_attribute_init_empty:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ *
+ * Initialize a PKCS#11 attribute to an empty state. The attribute
+ * type will be set, but no data will be set.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init_empty (GckAttribute *attr, gulong attr_type)
+{
+	g_return_if_fail (attr);
+	g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE));
+	memset (attr, 0, sizeof (GckAttribute));
+	attr->type = attr_type;
+	attr->length = 0;
+	attr->value = 0;
+}
+
+static void
+attribute_init_boolean (GckAttribute *attr, gulong attr_type,
+                        gboolean value, GckAllocator allocator)
+{
+	CK_BBOOL bvalue = value ? CK_TRUE : CK_FALSE;
+	attribute_init (attr, attr_type, &bvalue, sizeof (bvalue), allocator);
+}
+
+/**
+ * gck_attribute_init_boolean:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The boolean value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to boolean. This will result
+ * in a CK_BBOOL attribute from the PKCS#11 specs.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init_boolean (GckAttribute *attr, gulong attr_type,
+                             gboolean value)
+{
+	g_return_if_fail (attr);
+	attribute_init_boolean (attr, attr_type, value, g_realloc);
+}
+
+static void
+attribute_init_date (GckAttribute *attr, gulong attr_type,
+                     const GDate *value, GckAllocator allocator)
+{
+	gchar buffer[9];
+	CK_DATE date;
+	g_assert (value);
+	g_snprintf (buffer, sizeof (buffer), "%04d%02d%02d",
+	            (int)g_date_get_year (value),
+	            (int)g_date_get_month (value),
+	            (int)g_date_get_day (value));
+	memcpy (&date.year, buffer + 0, 4);
+	memcpy (&date.month, buffer + 4, 2);
+	memcpy (&date.day, buffer + 6, 2);
+	attribute_init (attr, attr_type, &date, sizeof (CK_DATE), allocator);
+}
+
+/**
+ * gck_attribute_init_date:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The date value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to a date. This will result
+ * in a CK_DATE attribute from the PKCS#11 specs.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init_date (GckAttribute *attr, gulong attr_type,
+                          const GDate *value)
+{
+	g_return_if_fail (attr);
+	g_return_if_fail (value);
+	attribute_init_date (attr, attr_type, value, g_realloc);
+}
+
+static void
+attribute_init_ulong (GckAttribute *attr, gulong attr_type,
+                      gulong value, GckAllocator allocator)
+{
+	CK_ULONG uvalue = value;
+	attribute_init (attr, attr_type, &uvalue, sizeof (uvalue), allocator);
+}
+
+/**
+ * gck_attribute_init_ulong:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The ulong value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to a unsigned long. This will result
+ * in a CK_ULONG attribute from the PKCS#11 specs.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init_ulong (GckAttribute *attr, gulong attr_type,
+                           gulong value)
+{
+	g_return_if_fail (attr);
+	attribute_init_ulong (attr, attr_type, value, g_realloc);
+}
+
+static void
+attribute_init_string (GckAttribute *attr, gulong attr_type,
+                       const gchar *value, GckAllocator allocator)
+{
+	gsize len = value ? strlen (value) : 0;
+	attribute_init (attr, attr_type, (gpointer)value, len, allocator);
+}
+
+/**
+ * gck_attribute_init_string:
+ * @attr: An uninitialized attribute.
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The null terminated string value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to a string. This will result
+ * in an attribute containing the text, but not the null terminator.
+ * The text in the attribute will be of the same encoding as you pass
+ * to this function.
+ *
+ * When done with the attribute you should use gck_attribute_clear()
+ * to free the internal memory.
+ **/
+void
+gck_attribute_init_string (GckAttribute *attr, gulong attr_type,
+                            const gchar *value)
+{
+	g_return_if_fail (attr);
+	attribute_init_string (attr, attr_type, value, g_realloc);
+}
+
+/**
+ * gck_attribute_new:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The raw value of the attribute.
+ * @length: The length of the attribute.
+ *
+ * Create a new PKCS#11 attribute. The value will be copied
+ * into the new attribute.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ **/
+GckAttribute*
+gck_attribute_new (gulong attr_type, gpointer value, gsize length)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	attribute_init (attr, attr_type, value, length, g_realloc);
+	return attr;
+}
+
+/**
+ * gck_attribute_new_invalid:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ *
+ * Create a new PKCS#11 attribute as 'invalid' or 'not found'
+ * state. Specifically this sets the value length to (CK_ULONG)-1
+ * as specified in the PKCS#11 specification.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ **/
+GckAttribute*
+gck_attribute_new_invalid (gulong attr_type)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	gck_attribute_init_invalid (attr, attr_type);
+	return attr;
+}
+
+/**
+ * gck_attribute_new_empty:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ *
+ * Create a new PKCS#11 attribute with empty data.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ */
+GckAttribute*
+gck_attribute_new_empty (gulong attr_type)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	gck_attribute_init_empty (attr, attr_type);
+	return attr;
+}
+
+/**
+ * gck_attribute_new_boolean:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The boolean value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to boolean. This will result
+ * in a CK_BBOOL attribute from the PKCS#11 specs.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ **/
+GckAttribute*
+gck_attribute_new_boolean (gulong attr_type, gboolean value)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	attribute_init_boolean (attr, attr_type, value, g_realloc);
+	return attr;
+}
+
+/**
+ * gck_attribute_new_date:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The date value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to a date. This will result
+ * in a CK_DATE attribute from the PKCS#11 specs.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ **/
+GckAttribute*
+gck_attribute_new_date (gulong attr_type, const GDate *value)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	attribute_init_date (attr, attr_type, value, g_realloc);
+	return attr;
+}
+
+/**
+ * gck_attribute_new_ulong:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The ulong value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to a unsigned long. This will result
+ * in a CK_ULONG attribute from the PKCS#11 specs.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ **/
+GckAttribute*
+gck_attribute_new_ulong (gulong attr_type, gulong value)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	attribute_init_ulong (attr, attr_type, value, g_realloc);
+	return attr;
+}
+
+/**
+ * gck_attribute_new_string:
+ * @attr_type: The PKCS#11 attribute type to set on the attribute.
+ * @value: The null terminated string value of the attribute.
+ *
+ * Initialize a PKCS#11 attribute to a string. This will result
+ * in an attribute containing the text, but not the null terminator.
+ * The text in the attribute will be of the same encoding as you pass
+ * to this function.
+ *
+ * Return value: The new attribute. When done with the attribute use
+ * gck_attribute_free() to free it.
+ **/
+GckAttribute*
+gck_attribute_new_string (gulong attr_type, const gchar *value)
+{
+	GckAttribute *attr = g_slice_new0 (GckAttribute);
+	attribute_init_string (attr, attr_type, value, g_realloc);
+	return attr;
+}
+
+/**
+ * gck_attribute_is_invalid:
+ * @attr: The attribute to check.
+ *
+ * Check if the PKCS#11 attribute represents 'invalid' or 'not found'
+ * according to the PKCS#11 spec. That is, having length
+ * of (CK_ULONG)-1.
+ *
+ * Return value: Whether the attribute represents invalid or not.
+ */
+gboolean
+gck_attribute_is_invalid (GckAttribute *attr)
+{
+	g_return_val_if_fail (attr, TRUE);
+	return attr->length == (gulong)-1;
+}
+
+/**
+ * gck_attribute_get_boolean:
+ * @attr: The attribute to retrieve value from.
+ *
+ * Get the CK_BBOOL of a PKCS#11 attribute. No conversion
+ * is performed. It is an error to pass an attribute to this
+ * function unless you're know it's supposed to contain a
+ * boolean value.
+ *
+ * Return value: The boolean value of the attribute.
+ */
+gboolean
+gck_attribute_get_boolean (GckAttribute *attr)
+{
+	g_return_val_if_fail (attr, FALSE);
+	if (gck_attribute_is_invalid (attr))
+		return FALSE;
+	g_return_val_if_fail (attr->length == sizeof (CK_BBOOL), FALSE);
+	g_return_val_if_fail (attr->value, FALSE);
+	return *((CK_BBOOL*)attr->value) == CK_TRUE ? TRUE : FALSE;
+}
+
+/**
+ * gck_attribute_get_ulong:
+ * @attr: The attribute to retrieve value from.
+ *
+ * Get the CK_ULONG value of a PKCS#11 attribute. No
+ * conversion is performed. It is an error to pass an attribute
+ * to this function unless you're know it's supposed to contain
+ * a value of the right type.
+ *
+ * Return value: The ulong value of the attribute.
+ */
+gulong
+gck_attribute_get_ulong (GckAttribute *attr)
+{
+	g_return_val_if_fail (attr, FALSE);
+	if (gck_attribute_is_invalid (attr))
+		return 0;
+	g_return_val_if_fail (attr->length == sizeof (CK_ULONG), (gulong)-1);
+	g_return_val_if_fail (attr->value, (gulong)-1);
+	return *((CK_ULONG*)attr->value);
+}
+
+/**
+ * gck_attribute_get_string:
+ * @attr: The attribute to retrieve value from.
+ *
+ * Get the string value of a PKCS#11 attribute. No
+ * conversion is performed. It is an error to pass an attribute
+ * to this function unless you're know it's supposed to contain
+ * a value of the right type.
+ *
+ * Return value: A null terminated string, to be freed with g_free(),
+ * or NULL if the value contained a NULL string.
+ */
+gchar*
+gck_attribute_get_string (GckAttribute *attr)
+{
+	g_return_val_if_fail (attr, NULL);
+
+	if (gck_attribute_is_invalid (attr))
+		return NULL;
+	if (!attr->value)
+		return NULL;
+
+	return g_strndup ((gchar*)attr->value, attr->length);
+}
+
+/**
+ * gck_attribute_get_date:
+ * @attr: The attribute to retrieve value from.
+ * @value: The date value to fill in with the parsed date.
+ *
+ * Get the CK_DATE of a PKCS#11 attribute. No
+ * conversion is performed. It is an error to pass an attribute
+ * to this function unless you're know it's supposed to contain
+ * a value of the right type.
+ */
+void
+gck_attribute_get_date (GckAttribute *attr, GDate *value)
+{
+	guint year, month, day;
+	gchar buffer[5];
+	CK_DATE *date;
+	gchar *end;
+
+	g_return_if_fail (attr);
+
+	if (gck_attribute_is_invalid (attr)) {
+		g_date_clear (value, 1);
+		return;
+	}
+
+	g_return_if_fail (attr->length == sizeof (CK_DATE));
+	g_return_if_fail (attr->value);
+	date = (CK_DATE*)attr->value;
+
+	memset (&buffer, 0, sizeof (buffer));
+	memcpy (buffer, date->year, 4);
+	year = strtol (buffer, &end, 10);
+	g_return_if_fail (end != buffer && !*end);
+
+	memset (&buffer, 0, sizeof (buffer));
+	memcpy (buffer, date->month, 2);
+	month = strtol (buffer, &end, 10);
+	g_return_if_fail (end != buffer && !*end);
+
+	memset (&buffer, 0, sizeof (buffer));
+	memcpy (buffer, date->day, 2);
+	day = strtol (buffer, &end, 10);
+	g_return_if_fail (end != buffer && !*end);
+
+	g_date_set_dmy (value, day, month, year);
+}
+
+/**
+ * gck_attribute_dup:
+ * @attr: The attribute to duplicate.
+ *
+ * Duplicate the PKCS#11 attribute. All value memory is
+ * also copied.
+ *
+ * Return value: The duplicated attribute. Use gck_attribute_free()
+ * to free it.
+ */
+GckAttribute*
+gck_attribute_dup (GckAttribute *attr)
+{
+	GckAttribute *copy;
+
+	if (!attr)
+		return NULL;
+
+	copy = g_slice_new0 (GckAttribute);
+	gck_attribute_init_copy (copy, attr);
+	return copy;
+}
+
+static void
+attribute_init_copy (GckAttribute *dest, const GckAttribute *src, GckAllocator allocator)
+{
+	g_assert (dest);
+	g_assert (src);
+	g_assert (allocator);
+
+	/*
+	 * TODO: Handle stupid, dumb, broken, special cases like
+	 * CKA_WRAP_TEMPLATE and CKA_UNWRAP_TEMPLATE.
+	 */
+
+	memcpy (dest, src, sizeof (GckAttribute));
+	if (src->value && src->length) {
+		dest->value = (allocator) (NULL, src->length);
+		g_assert (dest->value);
+		memcpy (dest->value, src->value, src->length);
+	}
+}
+
+/**
+ * gck_attribute_init_copy:
+ * @dest: An uninitialized attribute.
+ * @src: An attribute to copy.
+ *
+ * Initialize a PKCS#11 attribute as a copy of another attribute.
+ * This copies the value memory as well.
+ *
+ * When done with the copied attribute you should use
+ * gck_attribute_clear() to free the internal memory.
+ **/
+void
+gck_attribute_init_copy (GckAttribute *dest, const GckAttribute *src)
+{
+	g_return_if_fail (dest);
+	g_return_if_fail (src);
+	attribute_init_copy (dest, src, g_realloc);
+}
+
+static void
+attribute_clear (GckAttribute *attr, GckAllocator allocator)
+{
+	g_assert (attr);
+	g_assert (allocator);
+	if (attr->value)
+		(allocator) (attr->value, 0);
+	attr->value = NULL;
+	attr->length = 0;
+}
+
+/**
+ * gck_attribute_clear:
+ * @attr: Attribute to clear.
+ *
+ * Clear allocated memory held by a statically allocated attribute.
+ * These are usually initialized with gck_attribute_init() or a
+ * similar function.
+ *
+ * The type of the attribute will remain set.
+ **/
+void
+gck_attribute_clear (GckAttribute *attr)
+{
+	g_return_if_fail (attr);
+	attribute_clear (attr, g_realloc);
+}
+
+/**
+ * gck_attribute_free:
+ * @attr: Attribute to free.
+ *
+ * Free an attribute and its allocated memory. These is usually
+ * used with attributes that are allocated by gck_attribute_new()
+ * or a similar function.
+ **/
+void
+gck_attribute_free (GckAttribute *attr)
+{
+	if (attr) {
+		attribute_clear (attr, g_realloc);
+		g_slice_free (GckAttribute, attr);
+	}
+}
+
+/**
+ * SECTION:gck-attributes
+ * @title: GckAttributes
+ * @short_description: A set of PKCS11 attributes.
+ *
+ * A set of GckAttribute structures. These attributes contain information
+ * about a PKCS11 object. Use gck_object_get() or gck_object_set() to set and retrieve
+ * attributes on an object.
+ */
+
+/**
+ * GckAttributes:
+ *
+ * A set of GckAttribute structures.
+ */
+struct _GckAttributes {
+	GArray *array;
+	GckAllocator allocator;
+	gboolean locked;
+	gint refs;
+};
+
+/**
+ * gck_BOOLEAN:
+ *
+ * The attribute data is a gboolean. Used with variable argument functions.
+ **/
+
+/**
+ * gck_ULONG:
+ *
+ * The attribute data is a gulong. Used with variable argument functions.
+ **/
+
+/**
+ * gck_STRING:
+ *
+ * The attribute data is a gchar. Used with variable argument functions.
+ **/
+
+/**
+ * gck_DATE:
+ *
+ * The attribute data is a GDate. Used with variable argument functions.
+ **/
+
+/**
+ * gck_DATE:
+ *
+ * Signifies that no more attributes follow. Used with variable argument functions.
+ **/
+
+/**
+ * gck_ATTRIBUTES_TYPE:
+ *
+ * A boxed type that can be used to hold a GckAttributes object.
+ **/
+
+/**
+ * GckAllocator:
+ * @data: Memory to allocate or deallocate.
+ * @length: New length of memory.
+ *
+ * An allocator used to allocate data for the attributes in this GckAttributes set.
+ *
+ * This is a function that acts like g_realloc. Specifically it frees when length is
+ * set to zero, it allocates when data is set to NULL, and it reallocates when both
+ * are valid.
+ *
+ * Returns: The allocated memory, or NULL when freeing.
+ **/
+
+/**
+ * gck_attributes_get_boxed_type:
+ *
+ * Get the boxed type representing a GckAttributes array.
+ *
+ * Return value: The boxed type.
+ **/
+GType
+gck_attributes_get_boxed_type (void)
+{
+	static GType type = 0;
+	if (!type)
+		type = g_boxed_type_register_static ("GckAttributes",
+		                                     (GBoxedCopyFunc)gck_attributes_ref,
+		                                     (GBoxedFreeFunc)gck_attributes_unref);
+	return type;
+}
+
+/**
+ * gck_attributes_new:
+ *
+ * Create a new GckAttributes array.
+ *
+ * Return value: The new attributes array. When done with the array
+ * release it with gck_attributes_unref().
+ **/
+GckAttributes*
+gck_attributes_new (void)
+{
+	return gck_attributes_new_full (g_realloc);
+}
+
+/**
+ * gck_attributes_new_full:
+ * @allocator: Memory allocator for attribute data, or NULL for default.
+ *
+ * Create a new GckAttributes array.
+ *
+ * Return value: The new attributes array. When done with the array
+ * release it with gck_attributes_unref().
+ **/
+GckAttributes*
+gck_attributes_new_full (GckAllocator allocator)
+{
+	GckAttributes *attrs;
+
+	if (!allocator)
+		allocator = g_realloc;
+
+	g_assert (sizeof (GckAttribute) == sizeof (CK_ATTRIBUTE));
+	attrs = g_slice_new0 (GckAttributes);
+	attrs->array = g_array_new (0, 1, sizeof (GckAttribute));
+	attrs->allocator = allocator;
+	attrs->refs = 1;
+	attrs->locked = FALSE;
+	return attrs;
+}
+
+/**
+ * gck_attributes_new_empty:
+ * @attr_type: The first attribute type to add as empty.
+ * @...: The arguments should be values of attribute types, terminated with gck_INVALID.
+ *
+ * Creates an GckAttributes array with empty attributes. The arguments
+ * should be values of attribute types, terminated with gck_INVALID.
+ *
+ * Return value: The new attributes array. When done with the array
+ * release it with gck_attributes_unref().
+ **/
+GckAttributes*
+gck_attributes_new_empty (gulong attr_type, ...)
+{
+	GckAttributes *attrs = gck_attributes_new_full (g_realloc);
+	va_list va;
+
+	va_start (va, attr_type);
+
+	while (attr_type != GCK_INVALID) {
+		gck_attributes_add_empty (attrs, attr_type);
+		attr_type = va_arg (va, gulong);
+	}
+
+	va_end (va);
+
+	return attrs;
+}
+
+static GckAttributes*
+initialize_from_valist (GckAllocator allocator, gulong type, va_list va)
+{
+	GckAttributes *attrs;
+	gssize length;
+	gpointer value;
+
+	g_assert (allocator);
+
+	attrs = gck_attributes_new_full (allocator);
+
+	/* No attributes */
+	if (type == GCK_INVALID)
+		return attrs;
+
+	do {
+		length = va_arg (va, gssize);
+
+		/* All the different set types */
+		switch (length) {
+		case GCK_BOOLEAN:
+			gck_attributes_add_boolean (attrs, type, va_arg (va, gboolean));
+			break;
+		case GCK_ULONG:
+			gck_attributes_add_ulong (attrs, type, va_arg (va, gulong));
+			break;
+		case GCK_STRING:
+			gck_attributes_add_string (attrs, type, va_arg (va, const gchar*));
+			break;
+		case GCK_DATE:
+			gck_attributes_add_date (attrs, type, va_arg (va, const GDate*));
+			break;
+
+		/* Otherwise it should be data */
+		default:
+			value = va_arg (va, gpointer);
+
+			/* But not this long */
+			if (length < 0 || length >= G_MAXSSIZE)
+				g_warning ("length passed to attributes varargs is invalid or too large: %d", (int)length);
+			else
+				gck_attributes_add_data (attrs, type, value, length);
+			break;
+		};
+
+		type = va_arg (va, gulong);
+
+	} while (type != GCK_INVALID);
+
+	return attrs;
+}
+
+/**
+ * gck_attributes_newv:
+ * @attr_type: The first attribute type.
+ * @...: The arguments must be triples of: attribute type, data type, value
+ *
+ * Create a new GckAttributes array, containing a list of attributes.
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of gck_BOOLEAN, gck_ULONG,
+ * 				gck_STRING, gck_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with gck_INVALID.</para>
+ *
+ * Return value: The new attributes array. When done with the array
+ * release it with gck_attributes_unref().
+ **/
+GckAttributes*
+gck_attributes_newv (gulong attr_type, ...)
+{
+	GckAttributes *attrs;
+	va_list va;
+
+	va_start (va, attr_type);
+	attrs = initialize_from_valist (g_realloc, attr_type, va);
+	va_end (va);
+
+	return attrs;
+}
+
+/**
+ * gck_attributes_new_valist:
+ * @allocator: Memory allocator for attribute data, or NULL for default.
+ * @va: Variable argument containing attributes to add.
+ *
+ * Create a new GckAttributes array.
+ *
+ * The arguments must be triples of: attribute type, data type, value
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of gck_BOOLEAN, gck_ULONG,
+ * 				gck_STRING, gck_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with gck_INVALID.</para>
+ *
+ * Return value: The new attributes array. When done with the array
+ * release it with gck_attributes_unref().
+ **/
+GckAttributes*
+gck_attributes_new_valist (GckAllocator allocator, va_list va)
+{
+	gulong type = va_arg (va, gulong);
+
+	if (!allocator)
+		allocator = g_realloc;
+
+	return initialize_from_valist (allocator, type, va);
+}
+
+/**
+ * gck_attributes_at:
+ * @attrs: The attributes array.
+ * @index: The attribute index to retrieve.
+ *
+ * Get attribute at the specified index in the attribute array.
+ *
+ * Use gck_attributes_count() to determine how many attributes are
+ * in the array.
+ *
+ * Return value: The specified attribute.
+ **/
+GckAttribute*
+gck_attributes_at (GckAttributes *attrs, guint index)
+{
+	g_return_val_if_fail (attrs && attrs->array, NULL);
+	g_return_val_if_fail (index < attrs->array->len, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	return &g_array_index (attrs->array, GckAttribute, index);
+}
+
+static GckAttribute*
+attributes_push (GckAttributes *attrs)
+{
+	GckAttribute attr;
+	g_assert (!attrs->locked);
+	memset (&attr, 0, sizeof (attr));
+	g_array_append_val (attrs->array, attr);
+	return &g_array_index (attrs->array, GckAttribute, attrs->array->len - 1);
+}
+
+/**
+ * gck_attributes_add:
+ * @attrs: The attributes array to add to
+ * @attr: The attribute to add.
+ *
+ * Add the specified attribute to the array.
+ *
+ * The value stored in the attribute will be copied.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add (GckAttributes *attrs, GckAttribute *attr)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs && attrs->array, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	g_return_val_if_fail (attr, NULL);
+	added = attributes_push (attrs);
+	attribute_init_copy (added, attr, attrs->allocator);
+	return added;
+}
+
+/**
+ * gck_attributes_add_data:
+ * @attrs: The attributes array to add to.
+ * @attr_type: The type of attribute to add.
+ * @value: The raw memory of the attribute value.
+ * @length: The length of the attribute value.
+ *
+ * Add an attribute with the specified type and value to the array.
+ *
+ * The value stored in the attribute will be copied.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_data (GckAttributes *attrs, gulong attr_type,
+                          gconstpointer value, gsize length)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	attribute_init (added, attr_type, value, length, attrs->allocator);
+	return added;
+}
+
+/**
+ * gck_attributes_add_invalid:
+ * @attrs: The attributes array to add to.
+ * @attr_type: The type of attribute to add.
+ *
+ * Add an attribute with the specified type and an 'invalid' value to the array.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_invalid (GckAttributes *attrs, gulong attr_type)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	gck_attribute_init_invalid (added, attr_type);
+	return added;
+}
+
+/**
+ * gck_attributes_add_empty:
+ * @attrs: The attributes array to add.
+ * @attr_type: The type of attribute to add.
+ *
+ * Add an attribute with the specified type, with empty data.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_empty (GckAttributes *attrs, gulong attr_type)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	gck_attribute_init_empty (added, attr_type);
+	return added;
+}
+
+/**
+ * gck_attributes_add_boolean:
+ * @attrs: The attributes array to add to.
+ * @attr_type: The type of attribute to add.
+ * @value: The boolean value to add.
+ *
+ * Add an attribute with the specified type and value to the array.
+ *
+ * The value will be stored as a CK_BBOOL PKCS#11 style attribute.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_boolean (GckAttributes *attrs, gulong attr_type, gboolean value)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	attribute_init_boolean (added, attr_type, value, attrs->allocator);
+	return added;
+}
+
+/**
+ * gck_attributes_add_string:
+ * @attrs: The attributes array to add to.
+ * @attr_type: The type of attribute to add.
+ * @value: The null terminated string value to add.
+ *
+ * Add an attribute with the specified type and value to the array.
+ *
+ * The value will be copied into the attribute.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_string (GckAttributes *attrs, gulong attr_type, const gchar *value)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	attribute_init_string (added, attr_type, value, attrs->allocator);
+	return added;
+}
+
+/**
+ * gck_attributes_add_date:
+ * @attrs: The attributes array to add to.
+ * @attr_type: The type of attribute to add.
+ * @value: The GDate value to add.
+ *
+ * Add an attribute with the specified type and value to the array.
+ *
+ * The value will be stored as a CK_DATE PKCS#11 style attribute.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_date (GckAttributes *attrs, gulong attr_type, const GDate *value)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	attribute_init_date (added, attr_type, value, attrs->allocator);
+	return added;
+}
+
+/**
+ * gck_attributes_add_ulong:
+ * @attrs: The attributes array to add to.
+ * @attr_type: The type of attribute to add.
+ * @value: The gulong value to add.
+ *
+ * Add an attribute with the specified type and value to the array.
+ *
+ * The value will be stored as a CK_ULONG PKCS#11 style attribute.
+ *
+ * Return value: The attribute that was added.
+ **/
+GckAttribute*
+gck_attributes_add_ulong (GckAttributes *attrs, gulong attr_type, gulong value)
+{
+	GckAttribute *added;
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+	added = attributes_push (attrs);
+	attribute_init_ulong (added, attr_type, value, attrs->allocator);
+	return added;
+}
+
+/**
+ * gck_attributes_count:
+ * @attrs: The attributes array to count.
+ *
+ * Get the number of attributes in this attribute array.
+ *
+ * Return value: The number of contained attributes.
+ **/
+gulong
+gck_attributes_count (GckAttributes *attrs)
+{
+	g_return_val_if_fail (attrs, 0);
+	g_return_val_if_fail (!attrs->locked, 0);
+	return attrs->array->len;
+}
+
+/**
+ * gck_attributes_find:
+ * @attrs: The attributes array to search.
+ * @attr_type: The type of attribute to find.
+ *
+ * Find an attribute with the specified type in the array.
+ *
+ * Return value: The first attribute found with the specified type, or NULL.
+ **/
+GckAttribute*
+gck_attributes_find (GckAttributes *attrs, gulong attr_type)
+{
+	GckAttribute *attr;
+	guint i;
+
+	g_return_val_if_fail (attrs && attrs->array, NULL);
+	g_return_val_if_fail (!attrs->locked, NULL);
+
+	for (i = 0; i < attrs->array->len; ++i) {
+		attr = gck_attributes_at (attrs, i);
+		if (attr->type == attr_type)
+			return attr;
+	}
+
+	return NULL;
+}
+
+/**
+ * gck_attributes_find_boolean:
+ * @attrs: The attributes array to search.
+ * @attr_type: The type of attribute to find.
+ * @value: The resulting gboolean value.
+ *
+ * Find an attribute with the specified type in the array.
+ *
+ * The attribute (if found) must be of the right size to store
+ * a boolean value (ie: CK_BBOOL). If the attribute is marked invalid
+ * then it will be treated as not found.
+ *
+ * Return value: Whether a value was found or not.
+ **/
+gboolean
+gck_attributes_find_boolean (GckAttributes *attrs, gulong attr_type, gboolean *value)
+{
+	GckAttribute *attr;
+
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (!attrs->locked, FALSE);
+
+	attr = gck_attributes_find (attrs, attr_type);
+	if (!attr || gck_attribute_is_invalid (attr))
+		return FALSE;
+	*value = gck_attribute_get_boolean (attr);
+	return TRUE;
+}
+
+/**
+ * gck_attributes_find_ulong:
+ * @attrs: The attributes array to search.
+ * @attr_type: The type of attribute to find.
+ * @value: The resulting gulong value.
+ *
+ * Find an attribute with the specified type in the array.
+ *
+ * The attribute (if found) must be of the right size to store
+ * a unsigned long value (ie: CK_ULONG). If the attribute is marked invalid
+ * then it will be treated as not found.
+ *
+ * Return value: Whether a value was found or not.
+ **/
+gboolean
+gck_attributes_find_ulong (GckAttributes *attrs, gulong attr_type, gulong *value)
+{
+	GckAttribute *attr;
+
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (!attrs->locked, FALSE);
+
+	attr = gck_attributes_find (attrs, attr_type);
+	if (!attr || gck_attribute_is_invalid (attr))
+		return FALSE;
+	*value = gck_attribute_get_ulong (attr);
+	return TRUE;
+}
+
+/**
+ * gck_attributes_find_string:
+ * @attrs: The attributes array to search.
+ * @attr_type: The type of attribute to find.
+ * @value: The resulting string value.
+ *
+ * Find an attribute with the specified type in the array.
+ *
+ * If the attribute is marked invalid then it will be treated as not found.
+ * The resulting string will be null-terminated, and must be freed by the caller
+ * using g_free().
+ *
+ * Return value: Whether a value was found or not.
+ **/
+gboolean
+gck_attributes_find_string (GckAttributes *attrs, gulong attr_type, gchar **value)
+{
+	GckAttribute *attr;
+
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (!attrs->locked, FALSE);
+
+	attr = gck_attributes_find (attrs, attr_type);
+	if (!attr || gck_attribute_is_invalid (attr))
+		return FALSE;
+	*value = gck_attribute_get_string (attr);
+	return TRUE;
+}
+
+/**
+ * gck_attributes_find_date:
+ * @attrs: The attributes array to search.
+ * @attr_type: The type of attribute to find.
+ * @value: The resulting GDate value.
+ *
+ * Find an attribute with the specified type in the array.
+ *
+ * The attribute (if found) must be of the right size to store
+ * a date value (ie: CK_DATE). If the attribute is marked invalid
+ * then it will be treated as not found.
+ *
+ * Return value: Whether a value was found or not.
+ **/
+gboolean
+gck_attributes_find_date (GckAttributes *attrs, gulong attr_type, GDate *value)
+{
+	GckAttribute *attr;
+
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (!attrs->locked, FALSE);
+
+	attr = gck_attributes_find (attrs, attr_type);
+	if (!attr || gck_attribute_is_invalid (attr))
+		return FALSE;
+	gck_attribute_get_date (attr, value);
+	return TRUE;
+}
+
+/**
+ * gck_attributes_ref:
+ * @attrs: An attribute array
+ *
+ * Reference this attributes array.
+ *
+ * Returns: The attributes.
+ **/
+GckAttributes*
+gck_attributes_ref (GckAttributes *attrs)
+{
+	g_return_val_if_fail (attrs, NULL);
+	g_atomic_int_inc (&attrs->refs);
+	return attrs;
+}
+
+/**
+ * gck_attributes_unref:
+ * @attrs: An attribute array
+ *
+ * Unreference this attribute array.
+ *
+ * When all outstanding references are NULL, the array will be freed.
+ */
+void
+gck_attributes_unref (GckAttributes *attrs)
+{
+	guint i;
+
+	if (!attrs)
+		return;
+
+	if (g_atomic_int_dec_and_test (&attrs->refs)) {
+		g_return_if_fail (attrs->array);
+		g_return_if_fail (!attrs->locked);
+		for (i = 0; i < attrs->array->len; ++i)
+			attribute_clear (gck_attributes_at (attrs, i), attrs->allocator);
+		g_array_free (attrs->array, TRUE);
+		attrs->array = NULL;
+		g_slice_free (GckAttributes, attrs);
+	}
+}
+
+/* -------------------------------------------------------------------------------------------
+ * INTERNAL
+ *
+ * The idea is that while we're processing a GckAttributes array (via PKCS#11
+ * C_GetAtributeValue for example) the calling application shouldn't access those
+ * attributes at all, except to ref or unref them.
+ *
+ * We try to help debug this with our 'locked' states. The various processing
+ * functions that accept GckAttributes lock the attributes while handing
+ * them off to be processed (perhaps in a different thread). We check this locked
+ * flag in all public functions accessing GckAttributes.
+ *
+ * The reason we don't use thread safe or atomic primitives here, is because:
+ *  a) The attributes are 'locked' by the same thread that prepares the call.
+ *  b) This is a debugging feature, and should not be relied on for correctness.
+ */
+
+void
+_gck_attributes_lock (GckAttributes *attrs)
+{
+	g_assert (attrs);
+	g_assert (!attrs->locked);
+	attrs->locked = TRUE;
+}
+
+void
+_gck_attributes_unlock (GckAttributes *attrs)
+{
+	g_assert (attrs);
+	g_assert (attrs->locked);
+	attrs->locked = FALSE;
+}
+
+CK_ATTRIBUTE_PTR
+_gck_attributes_prepare_in (GckAttributes *attrs, CK_ULONG_PTR n_attrs)
+{
+	GckAttribute *attr;
+	guint i;
+
+	g_assert (attrs);
+	g_assert (n_attrs);
+	g_assert (attrs->locked);
+
+	/* Prepare the attributes to receive their length */
+
+	for (i = 0; i < attrs->array->len; ++i) {
+		attr = &g_array_index (attrs->array, GckAttribute, i);
+		attribute_clear (attr, attrs->allocator);
+	}
+
+	*n_attrs = attrs->array->len;
+	return (CK_ATTRIBUTE_PTR)attrs->array->data;
+}
+
+CK_ATTRIBUTE_PTR
+_gck_attributes_commit_in (GckAttributes *attrs, CK_ULONG_PTR n_attrs)
+{
+	GckAttribute *attr;
+	guint i;
+
+	g_assert (attrs);
+	g_assert (n_attrs);
+	g_assert (attrs->locked);
+
+	/* Allocate each attribute with the length that was set */
+
+	for (i = 0; i < attrs->array->len; ++i) {
+		attr = &g_array_index (attrs->array, GckAttribute, i);
+		g_assert (!attr->value);
+		if (attr->length != 0 && attr->length != (gulong)-1) {
+			attr->value = (attrs->allocator) (NULL, attr->length);
+			g_assert (attr->value);
+		}
+	}
+
+	*n_attrs = attrs->array->len;
+	return (CK_ATTRIBUTE_PTR)attrs->array->data;
+}
+
+CK_ATTRIBUTE_PTR
+_gck_attributes_commit_out (GckAttributes *attrs, CK_ULONG_PTR n_attrs)
+{
+	g_assert (attrs);
+	g_assert (n_attrs);
+	g_assert (attrs->locked);
+
+	*n_attrs = attrs->array->len;
+	return (CK_ATTRIBUTE_PTR)attrs->array->data;
+}
diff --git a/gck/gck-call.c b/gck/gck-call.c
new file mode 100644
index 0000000..939d81b
--- /dev/null
+++ b/gck/gck-call.c
@@ -0,0 +1,541 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-call.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-private.h"
+
+#include <string.h>
+
+typedef struct _GckCallSource GckCallSource;
+
+static gpointer _gck_call_parent_class = NULL;
+
+struct _GckCall {
+	GObject parent;
+	GckModule *module;
+
+	/* For making the call */
+	GckPerformFunc perform;
+	GckCompleteFunc complete;
+	GckArguments *args;
+	GCancellable *cancellable;
+	GDestroyNotify destroy;
+	CK_RV rv;
+
+	/* For result callback only */
+	gpointer object;
+	GAsyncReadyCallback callback;
+	gpointer user_data;
+};
+
+struct _GckCallClass {
+	GObjectClass parent;
+	GThreadPool *thread_pool;
+	GAsyncQueue *completed_queue;
+	guint completed_id;
+};
+
+struct _GckCallSource {
+	GSource source;
+	GckCallClass *klass;
+};
+
+/* ----------------------------------------------------------------------------
+ * HELPER FUNCTIONS
+ */
+
+static CK_RV
+perform_call (GckPerformFunc func, GCancellable *cancellable, GckArguments *args)
+{
+	CK_RV rv;
+
+	/* Double check a few things */
+	g_assert (func);
+	g_assert (args);
+
+	if (cancellable) {
+		if (g_cancellable_is_cancelled (cancellable)) {
+			return CKR_FUNCTION_CANCELED;
+		}
+
+		/* Push for the notify callback */
+		g_object_ref (cancellable);
+		g_cancellable_push_current (cancellable);
+	}
+
+	rv = (func) (args);
+
+	if (cancellable) {
+		g_cancellable_pop_current (cancellable);
+		g_object_unref (cancellable);
+	}
+
+	return rv;
+}
+
+static gboolean
+complete_call (GckCompleteFunc func, GckArguments *args, CK_RV result)
+{
+	/* Double check a few things */
+	g_assert (args);
+
+	/* If no complete function, then just ignore */
+	if (!func)
+		return TRUE;
+
+	return (func) (args, result);
+}
+
+
+static void
+process_async_call (gpointer data, GckCallClass *klass)
+{
+	GckCall *call = GCK_CALL (data);
+
+	g_assert (GCK_IS_CALL (call));
+
+	call->rv = perform_call (call->perform, call->cancellable, call->args);
+
+	g_async_queue_push (klass->completed_queue, call);
+
+	/* Wakeup main thread if on a separate thread */
+	g_main_context_wakeup (NULL);
+}
+
+static void
+process_result (GckCall *call, gpointer unused)
+{
+	gboolean stop = FALSE;
+
+	/* Double check a few things */
+	g_assert (GCK_IS_CALL (call));
+
+	if (call->cancellable) {
+		/* Don't call the callback when cancelled */
+		if (g_cancellable_is_cancelled (call->cancellable)) {
+			call->rv = CKR_FUNCTION_CANCELED;
+			stop = TRUE;
+		}
+	}
+
+	/*
+	 * Hmmm, does the function want to actually be done?
+	 * If not, then queue this call again.
+	 */
+	if (!stop && !complete_call (call->complete, call->args, call->rv)) {
+		g_object_ref (call);
+		g_thread_pool_push (GCK_CALL_GET_CLASS (call)->thread_pool, call, NULL);
+
+	/* All done, finish processing */
+	} else if (call->callback) {
+		g_assert (G_IS_OBJECT (call->object));
+		(call->callback) (G_OBJECT (call->object), G_ASYNC_RESULT (call),
+				  call->user_data);
+	}
+}
+
+static gboolean
+process_completed (GckCallClass *klass)
+{
+	gpointer call;
+
+	g_assert (klass->completed_queue);
+
+	call = g_async_queue_try_pop (klass->completed_queue);
+	if (call) {
+		process_result (call, NULL);
+		g_object_unref (call);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+completed_prepare(GSource* base, gint *timeout)
+{
+	GckCallSource *source = (GckCallSource*)base;
+	gboolean have;
+
+	g_assert (source->klass->completed_queue);
+	have = g_async_queue_length (source->klass->completed_queue) > 0;
+	*timeout = have ? 0 : -1;
+	return have;
+}
+
+static gboolean
+completed_check(GSource* base)
+{
+	GckCallSource *source = (GckCallSource*)base;
+	g_assert (source->klass->completed_queue);
+	return g_async_queue_length (source->klass->completed_queue) > 0;
+}
+
+static gboolean
+completed_dispatch(GSource* base, GSourceFunc callback, gpointer user_data)
+{
+	GckCallSource *source = (GckCallSource*)base;
+	process_completed (source->klass);
+	return TRUE;
+}
+
+static void
+completed_finalize(GSource* base)
+{
+
+}
+
+static GSourceFuncs completed_functions = {
+	completed_prepare,
+	completed_check,
+	completed_dispatch,
+	completed_finalize
+};
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+_gck_call_init (GckCall *call)
+{
+	call->rv = CKR_OK;
+}
+
+static void
+_gck_call_finalize (GObject *obj)
+{
+	GckCall *call = GCK_CALL (obj);
+
+	if (call->module)
+		g_object_unref (call->module);
+	call->module = NULL;
+
+	if (call->object)
+		g_object_unref (call->object);
+	call->object = NULL;
+
+	if (call->cancellable)
+		g_object_unref (call->cancellable);
+	call->cancellable = NULL;
+
+	if (call->destroy)
+		(call->destroy) (call->args);
+	call->destroy = NULL;
+	call->args = NULL;
+
+	G_OBJECT_CLASS (_gck_call_parent_class)->finalize (obj);
+}
+
+static gpointer
+_gck_call_get_user_data (GAsyncResult *async_result)
+{
+	g_return_val_if_fail (GCK_IS_CALL (async_result), NULL);
+	return GCK_CALL (async_result)->user_data;
+}
+
+static GObject*
+_gck_call_get_source_object (GAsyncResult *async_result)
+{
+	g_return_val_if_fail (GCK_IS_CALL (async_result), NULL);
+	return GCK_CALL (async_result)->object;
+}
+
+static void
+_gck_call_implement_async_result (GAsyncResultIface *iface)
+{
+	iface->get_user_data = _gck_call_get_user_data;
+	iface->get_source_object = _gck_call_get_source_object;
+}
+
+static void
+_gck_call_class_init (GckCallClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+
+	_gck_call_parent_class = g_type_class_peek_parent (klass);
+	gobject_class->finalize = _gck_call_finalize;
+}
+
+static void
+_gck_call_base_init (GckCallClass *klass)
+{
+	GckCallSource *source;
+	GMainContext *context;
+	GError *err = NULL;
+
+	klass->thread_pool = g_thread_pool_new ((GFunc)process_async_call, klass, 16, FALSE, &err);
+	if (!klass->thread_pool) {
+		g_critical ("couldn't create thread pool: %s",
+		            err && err->message ? err->message : "");
+		return;
+	}
+
+	klass->completed_queue = g_async_queue_new_full (g_object_unref);
+	g_assert (klass->completed_queue);
+
+	context = g_main_context_default ();
+	g_assert (context);
+
+	/* Add our idle handler which processes other tasks */
+	source = (GckCallSource*)g_source_new (&completed_functions, sizeof (GckCallSource));
+	source->klass = klass;
+	klass->completed_id = g_source_attach ((GSource*)source, context);
+	g_source_set_callback ((GSource*)source, NULL, NULL, NULL);
+	g_source_unref ((GSource*)source);
+}
+
+static void
+_gck_call_base_finalize (GckCallClass *klass)
+{
+	GMainContext *context;
+	GSource *src;
+
+	if (klass->thread_pool) {
+		g_assert (g_thread_pool_unprocessed (klass->thread_pool) == 0);
+		g_thread_pool_free (klass->thread_pool, FALSE, TRUE);
+		klass->thread_pool = NULL;
+	}
+
+	if (klass->completed_id) {
+		context = g_main_context_default ();
+		g_return_if_fail (context);
+
+		src = g_main_context_find_source_by_id (context, klass->completed_id);
+		g_assert (src);
+		g_source_destroy (src);
+		klass->completed_id = 0;
+	}
+
+	if (klass->completed_queue) {
+		g_assert (g_async_queue_length (klass->completed_queue));
+		g_async_queue_unref (klass->completed_queue);
+		klass->completed_queue = NULL;
+	}
+}
+
+GType
+_gck_call_get_type (void)
+{
+	static volatile gsize type_id__volatile = 0;
+
+	if (g_once_init_enter (&type_id__volatile)) {
+
+		static const GTypeInfo type_info = {
+			sizeof (GckCallClass),
+			(GBaseInitFunc)_gck_call_base_init,
+			(GBaseFinalizeFunc)_gck_call_base_finalize,
+			(GClassInitFunc)_gck_call_class_init,
+			(GClassFinalizeFunc)NULL,
+			NULL,   // class_data
+			sizeof (GckCall),
+			0,      // n_preallocs
+			(GInstanceInitFunc)_gck_call_init,
+		};
+
+		static const GInterfaceInfo interface_info = {
+			(GInterfaceInitFunc)_gck_call_implement_async_result
+		};
+
+		GType type_id = g_type_register_static (G_TYPE_OBJECT, "_GckCall", &type_info, 0);
+		g_type_add_interface_static (type_id, G_TYPE_ASYNC_RESULT, &interface_info);
+
+		g_once_init_leave (&type_id__volatile, type_id);
+	}
+
+	return type_id__volatile;
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+void
+_gck_call_uninitialize (void)
+{
+
+}
+
+gboolean
+_gck_call_sync (gpointer object, gpointer perform, gpointer complete,
+                 gpointer data, GCancellable *cancellable, GError **err)
+{
+	GckArguments *args = (GckArguments*)data;
+	GckModule *module = NULL;
+	CK_RV rv;
+
+	g_assert (G_IS_OBJECT (object));
+	g_assert (perform);
+	g_assert (args);
+
+	g_object_get (object, "module", &module, "handle", &args->handle, NULL);
+	g_assert (GCK_IS_MODULE (module));
+
+	/* We now hold a reference to module until below */
+	args->pkcs11 = gck_module_get_functions (module);
+	g_assert (args->pkcs11);
+
+	do {
+		rv = perform_call (perform, cancellable, args);
+		if (rv == CKR_FUNCTION_CANCELED)
+			break;
+
+	} while (!complete_call (complete, args, rv));
+
+	g_object_unref (module);
+
+	if (rv == CKR_OK)
+		return TRUE;
+
+	g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv));
+	return FALSE;
+}
+
+gpointer
+_gck_call_async_prep (gpointer object, gpointer cb_object, gpointer perform,
+                       gpointer complete, gsize args_size, gpointer destroy)
+{
+	GckArguments *args;
+	GckCall *call;
+
+	g_assert (!object || G_IS_OBJECT (object));
+	g_assert (perform);
+
+	if (!destroy)
+		destroy = g_free;
+
+	if (args_size == 0)
+		args_size = sizeof (GckArguments);
+	g_assert (args_size >= sizeof (GckArguments));
+
+	args = g_malloc0 (args_size);
+	call = g_object_new (GCK_TYPE_CALL, NULL);
+	call->destroy = (GDestroyNotify)destroy;
+	call->perform = (GckPerformFunc)perform;
+	call->complete = (GckCompleteFunc)complete;
+	call->object = cb_object;
+	g_object_ref (cb_object);
+
+	/* Hook the two together */
+	call->args = args;
+	call->args->call = call;
+
+	/* Setup call object if available */
+	if (object != NULL)
+		_gck_call_async_object (call, object);
+
+	return args;
+}
+
+void
+_gck_call_async_object (GckCall *call, gpointer object)
+{
+	g_assert (GCK_IS_CALL (call));
+	g_assert (call->args);
+
+	if (call->module)
+		g_object_unref (call->module);
+	call->module = NULL;
+
+	g_object_get (object, "module", &call->module, "handle", &call->args->handle, NULL);
+	g_assert (GCK_IS_MODULE (call->module));
+	call->args->pkcs11 = gck_module_get_functions (call->module);
+
+	/* We now hold a reference on module until finalize */
+}
+
+GckCall*
+_gck_call_async_ready (gpointer data, GCancellable *cancellable,
+                        GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckArguments *args = (GckArguments*)data;
+	g_assert (GCK_IS_CALL (args->call));
+
+	args->call->cancellable = cancellable;
+	if (cancellable) {
+		g_assert (G_IS_CANCELLABLE (cancellable));
+		g_object_ref (cancellable);
+	}
+
+	args->call->callback = callback;
+	args->call->user_data = user_data;
+
+	return args->call;
+}
+
+void
+_gck_call_async_go (GckCall *call)
+{
+	g_assert (GCK_IS_CALL (call));
+	g_assert (call->args->pkcs11);
+
+	/* To keep things balanced, process at one completed event */
+	process_completed(GCK_CALL_GET_CLASS (call));
+
+	g_assert (GCK_CALL_GET_CLASS (call)->thread_pool);
+	g_thread_pool_push (GCK_CALL_GET_CLASS (call)->thread_pool, call, NULL);
+}
+
+void
+_gck_call_async_ready_go (gpointer data, GCancellable *cancellable,
+                           GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckCall *call = _gck_call_async_ready (data, cancellable, callback, user_data);
+	_gck_call_async_go (call);
+}
+
+gboolean
+_gck_call_basic_finish (GAsyncResult *result, GError **err)
+{
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_CALL (result), FALSE);
+
+	rv = GCK_CALL (result)->rv;
+	if (rv == CKR_OK)
+		return TRUE;
+
+	g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv));
+	return FALSE;
+}
+
+void
+_gck_call_async_short (GckCall *call, CK_RV rv)
+{
+	g_assert (GCK_IS_CALL (call));
+
+	call->rv = rv;
+
+	/* Already complete, so just push it for processing in main loop */
+	g_assert (GCK_CALL_GET_CLASS (call)->completed_queue);
+	g_async_queue_push (GCK_CALL_GET_CLASS (call)->completed_queue, call);
+	g_main_context_wakeup (NULL);
+}
+
+gpointer
+_gck_call_get_arguments (GckCall *call)
+{
+	g_assert (GCK_IS_CALL (call));
+	return call->args;
+}
diff --git a/gck/gck-marshal.list b/gck/gck-marshal.list
new file mode 100644
index 0000000..d9553af
--- /dev/null
+++ b/gck/gck-marshal.list
@@ -0,0 +1,3 @@
+BOOLEAN:STRING,POINTER
+BOOLEAN:OBJECT,STRING,POINTER
+BOOLEAN:ULONG
diff --git a/gck/gck-misc.c b/gck/gck-misc.c
new file mode 100644
index 0000000..ae8c66d
--- /dev/null
+++ b/gck/gck-misc.c
@@ -0,0 +1,437 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-misc.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+
+#include <glib/gi18n.h>
+
+/**
+ * SECTION:gck-error
+ * @title: Errors
+ * @short_description: Gck Errors and error codes.
+ *
+ * Errors are returned as GError structures. The code member of GError
+ * contains the raw PKCS11 CK_RV result value.
+ */
+
+/**
+ * GCK_VENDOR_CODE:
+ *
+ * Custom PKCS11 errors that originate from the gck library, are
+ * based at this error code.
+ */
+
+/**
+ * CKR_GCK_MODULE_PROBLEM:
+ *
+ * A result code that signifies there was a problem loading a PKCS11
+ * module, usually a shared library.
+ *
+ * More details can be found in the error string.
+ */
+
+/**
+ * GCK_ERROR:
+ *
+ * The error domain for gck library errors.
+ */
+GQuark
+gck_get_error_quark (void)
+{
+	static GQuark domain = 0;
+	static volatile gsize quark_inited = 0;
+
+	if (g_once_init_enter (&quark_inited)) {
+		domain = g_quark_from_static_string ("gck-error");
+		g_once_init_leave (&quark_inited, 1);
+	}
+
+	return domain;
+}
+
+/**
+ * gck_message_from_rv:
+ * @rv: The PKCS#11 return value to get a message for.
+ *
+ * Get a message for a PKCS#11 return value or error code. Do not
+ * pass CKR_OK or other such non errors to this function.
+ *
+ * Return value: The user readable message.
+ **/
+const gchar*
+gck_message_from_rv (CK_RV rv)
+{
+	switch (rv) {
+
+	/* These are not really errors, or not current */
+	case CKR_OK:
+	case CKR_NO_EVENT:
+	case CKR_FUNCTION_NOT_PARALLEL:
+	case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
+		g_return_val_if_reached ("");
+
+	case CKR_CANCEL:
+	case CKR_FUNCTION_CANCELED:
+		return _("The operation was cancelled");
+
+	case CKR_HOST_MEMORY:
+		return _("Insufficient memory available");
+	case CKR_SLOT_ID_INVALID:
+		return _("The specified slot ID is not valid");
+	case CKR_GENERAL_ERROR:
+		return _("Internal error");
+	case CKR_FUNCTION_FAILED:
+		return _("The operation failed");
+	case CKR_ARGUMENTS_BAD:
+		return _("Invalid arguments");
+	case CKR_NEED_TO_CREATE_THREADS:
+		return _("The module cannot create needed threads");
+	case CKR_CANT_LOCK:
+		return _("The module cannot lock data properly");
+	case CKR_ATTRIBUTE_READ_ONLY:
+		return _("The field is read-only");
+	case CKR_ATTRIBUTE_SENSITIVE:
+		return _("The field is sensitive and cannot be revealed");
+	case CKR_ATTRIBUTE_TYPE_INVALID:
+		return _("The field is invalid or does not exist");
+	case CKR_ATTRIBUTE_VALUE_INVALID:
+		return _("Invalid value for field");
+	case CKR_DATA_INVALID:
+		return _("The data is not valid or unrecognized");
+	case CKR_DATA_LEN_RANGE:
+		return _("The data is too long");
+	case CKR_DEVICE_ERROR:
+		return _("An error occurred on the device");
+	case CKR_DEVICE_MEMORY:
+		return _("Insufficient memory available on device");
+	case CKR_DEVICE_REMOVED:
+		return _("The device was removed or unplugged");
+	case CKR_ENCRYPTED_DATA_INVALID:
+		return _("The encrypted data is not valid or unrecognized");
+	case CKR_ENCRYPTED_DATA_LEN_RANGE:
+		return _("The encrypted data is too long");
+	case CKR_FUNCTION_NOT_SUPPORTED:
+		return _("This operation is not supported");
+	case CKR_KEY_HANDLE_INVALID:
+		return _("The key is missing or invalid");
+	case CKR_KEY_SIZE_RANGE:
+		return _("The key is the wrong size");
+	case CKR_KEY_TYPE_INCONSISTENT:
+		return _("The key is of the wrong type");
+	case CKR_KEY_NOT_NEEDED:
+		return _("No key is needed");
+	case CKR_KEY_CHANGED:
+		return _("The key is different than before");
+	case CKR_KEY_NEEDED:
+		return _("A key is needed");
+	case CKR_KEY_INDIGESTIBLE:
+		return _("Cannot include the key in digest");
+	case CKR_KEY_FUNCTION_NOT_PERMITTED:
+		return _("This operation cannot be done with this key");
+	case CKR_KEY_NOT_WRAPPABLE:
+		return _("The key cannot be wrapped");
+	case CKR_KEY_UNEXTRACTABLE:
+		return _("Cannot export this key");
+	case CKR_MECHANISM_INVALID:
+		return _("The crypto mechanism is invalid or unrecognized");
+	case CKR_MECHANISM_PARAM_INVALID:
+		return _("The crypto mechanism has an invalid argument");
+	case CKR_OBJECT_HANDLE_INVALID:
+		return _("The object is missing or invalid");
+	case CKR_OPERATION_ACTIVE:
+		return _("Another operation is already taking place");
+	case CKR_OPERATION_NOT_INITIALIZED:
+		return _("No operation is taking place");
+	case CKR_PIN_INCORRECT:
+		return _("The password or PIN is incorrect");
+	case CKR_PIN_INVALID:
+		return _("The password or PIN is invalid");
+	case CKR_PIN_LEN_RANGE:
+		return _("The password or PIN is of an invalid length");
+	case CKR_PIN_EXPIRED:
+		return _("The password or PIN has expired");
+	case CKR_PIN_LOCKED:
+		return _("The password or PIN is locked");
+	case CKR_SESSION_CLOSED:
+		return _("The session is closed");
+	case CKR_SESSION_COUNT:
+		return _("Too many sessions are active");
+	case CKR_SESSION_HANDLE_INVALID:
+		return _("The session is invalid");
+	case CKR_SESSION_READ_ONLY:
+		return _("The session is read-only");
+	case CKR_SESSION_EXISTS:
+		return _("An open session exists");
+	case CKR_SESSION_READ_ONLY_EXISTS:
+		return _("A read-only session exists");
+	case CKR_SESSION_READ_WRITE_SO_EXISTS:
+		return _("An administrator session exists");
+	case CKR_SIGNATURE_INVALID:
+		return _("The signature is bad or corrupted");
+	case CKR_SIGNATURE_LEN_RANGE:
+		return _("The signature is unrecognized or corrupted");
+	case CKR_TEMPLATE_INCOMPLETE:
+		return _("Certain required fields are missing");
+	case CKR_TEMPLATE_INCONSISTENT:
+		return _("Certain fields have invalid values");
+	case CKR_TOKEN_NOT_PRESENT:
+		return _("The device is not present or unplugged");
+	case CKR_TOKEN_NOT_RECOGNIZED:
+		return _("The device is invalid or unrecognizable");
+	case CKR_TOKEN_WRITE_PROTECTED:
+		return _("The device is write protected");
+	case CKR_UNWRAPPING_KEY_HANDLE_INVALID:
+		return _("Cannot import because the key is invalid");
+	case CKR_UNWRAPPING_KEY_SIZE_RANGE:
+		return _("Cannot import because the key is of the wrong size");
+	case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT:
+		return _("Cannot import because the key is of the wrong type");
+	case CKR_USER_ALREADY_LOGGED_IN:
+		return _("You are already logged in");
+	case CKR_USER_NOT_LOGGED_IN:
+		return _("No user has logged in");
+	case CKR_USER_PIN_NOT_INITIALIZED:
+		return _("The user's password or PIN is not set");
+	case CKR_USER_TYPE_INVALID:
+		return _("The user is of an invalid type");
+	case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
+		return _("Another user is already logged in");
+	case CKR_USER_TOO_MANY_TYPES:
+		return _("Too many users of different types logged in");
+	case CKR_WRAPPED_KEY_INVALID:
+		return _("Cannot import an invalid key");
+	case CKR_WRAPPED_KEY_LEN_RANGE:
+		return _("Cannot import a key of the wrong size");
+	case CKR_WRAPPING_KEY_HANDLE_INVALID:
+		return _("Cannot export because the key is invalid");
+	case CKR_WRAPPING_KEY_SIZE_RANGE:
+		return _("Cannot export because the key is of the wrong size");
+	case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:
+		return _("Cannot export because the key is of the wrong type");
+	case CKR_RANDOM_SEED_NOT_SUPPORTED:
+		return _("Unable to initialize the random number generator");
+	case CKR_RANDOM_NO_RNG:
+		return _("No random number generator available");
+	case CKR_DOMAIN_PARAMS_INVALID:
+		return _("The crypto mechanism has an invalid parameter");
+	case CKR_BUFFER_TOO_SMALL:
+		return _("Not enough space to store the result");
+	case CKR_SAVED_STATE_INVALID:
+		return _("The saved state is invalid");
+	case CKR_INFORMATION_SENSITIVE:
+		return _("The information is sensitive and cannot be revealed");
+	case CKR_STATE_UNSAVEABLE:
+		return _("The state cannot be saved");
+	case CKR_CRYPTOKI_NOT_INITIALIZED:
+		return _("The module has not been initialized");
+	case CKR_CRYPTOKI_ALREADY_INITIALIZED:
+		return _("The module has already been initialized");
+	case CKR_MUTEX_BAD:
+		return _("Cannot lock data");
+	case CKR_MUTEX_NOT_LOCKED:
+		return _("The data cannot be locked");
+	case CKR_FUNCTION_REJECTED:
+		return _("The signature request was rejected by the user");
+
+	default:
+		g_message ("unknown error: %lu", (gulong)rv);
+		return _("Unknown error");
+	}
+}
+
+/**
+ * SECTION:gck-misc
+ * @title: Miscellaneous Functions
+ * @short_description: Other miscellaneous functions.
+ *
+ * A few supporting functions that come in handy when dealing with the gck
+ * library or PKCS11 in general.
+ */
+
+/**
+ * gck_list_unref_free:
+ * @reflist: List of Gobject reference counted pointers.
+ *
+ * Free a list of GObject based pointers. All objects in the list
+ * will be unreffed and then the list itself will be freed.
+ **/
+void
+gck_list_unref_free (GList *reflist)
+{
+	GList *l;
+	for (l = reflist; l; l = g_list_next (l)) {
+		g_return_if_fail (G_IS_OBJECT (l->data));
+		g_object_unref (l->data);
+	}
+	g_list_free (reflist);
+}
+
+/**
+ * gck_list_ref_copy:
+ * @reflist: List of GObject reference counted objects.
+ *
+ * Copy a list of GObject based pointers. All objects
+ * in the list will be reffed and the list will be copied.
+ *
+ * Return value: The copied and reffed list. When done, free it with
+ * gck_list_unref_free ()
+ **/
+GList*
+gck_list_ref_copy (GList *reflist)
+{
+	GList *l, *copy = g_list_copy (reflist);
+	for (l = copy; l; l = g_list_next (l)) {
+		g_return_val_if_fail (G_IS_OBJECT (l->data), NULL);
+		g_object_ref (l->data);
+	}
+	return copy;
+}
+
+/**
+ * gck_string_from_chars:
+ * @data: The character data to turn into a null terminated string.
+ * @max: The maximum length of the charater data.
+ *
+ * Create a string from a set of PKCS#11 characters. This is
+ * similar to g_strndup, except for that it also strips trailing
+ * spaces. These space padded strings are often used in PKCS#11
+ * structures.
+ *
+ * Return value: The null terminated string.
+ */
+gchar*
+gck_string_from_chars (const guchar *data, gsize max)
+{
+	gchar *string;
+
+	g_return_val_if_fail (data, NULL);
+	g_return_val_if_fail (max, NULL);
+
+	string = g_strndup ((gchar*)data, max);
+	g_strchomp (string);
+	return string;
+}
+
+guint
+_gck_ulong_hash (gconstpointer v)
+{
+	const signed char *p = v;
+	guint32 i, h = *p;
+
+	for(i = 0; i < sizeof (gulong); ++i)
+		h = (h << 5) - h + *(p++);
+
+	return h;
+}
+
+gboolean
+_gck_ulong_equal (gconstpointer v1, gconstpointer v2)
+{
+	return *((const gulong*)v1) == *((const gulong*)v2);
+}
+
+static GQuark mechanism_quark = 0;
+
+static void
+free_refs (gpointer data)
+{
+	gint *refs = data;
+	g_assert (refs);
+	g_assert (*refs == 0);
+	g_slice_free (gint, data);
+}
+
+GckMechanism*
+gck_mechanism_new (gulong type)
+{
+	return gck_mechanism_new_with_param (type, NULL, 0);
+}
+
+GckMechanism*
+gck_mechanism_new_with_param (gulong type, gconstpointer parameter,
+                               gulong n_parameter)
+{
+	static volatile gsize inited_quark = 0;
+	GckMechanism *mech;
+	gint *refs;
+
+	/* Initialize first time around */
+	if (g_once_init_enter (&inited_quark)) {
+		mechanism_quark = g_quark_from_static_string ("GckMechanism::refs");
+		g_once_init_leave (&inited_quark, 1);
+	}
+
+	mech = g_slice_new (GckMechanism);
+	mech->type = type;
+	mech->parameter = g_memdup (parameter, n_parameter);
+	mech->n_parameter = n_parameter;
+
+	refs = g_slice_new (gint);
+	*refs = 1;
+	g_dataset_id_set_data_full (mech, mechanism_quark, refs, free_refs);
+
+	return mech;
+}
+
+GckMechanism*
+gck_mechanism_ref (GckMechanism* mech)
+{
+	gint *refs;
+
+	g_return_val_if_fail (mech, NULL);
+
+	refs = g_dataset_id_get_data (mech, mechanism_quark);
+	if (refs == NULL) {
+		g_warning ("Encountered invalid GckMechanism struct. Either it was unreffed or "
+		           "possibly allocated on the stack. Always use gck_mechanism_new () and friends.");
+		return NULL;
+	}
+
+	g_atomic_int_add (refs, 1);
+	return mech;
+}
+
+void
+gck_mechanism_unref (GckMechanism* mech)
+{
+	gint *refs;
+
+	if (!mech)
+		return;
+
+	refs = g_dataset_id_get_data (mech, mechanism_quark);
+	if (refs == NULL) {
+		g_warning ("Encountered invalid GckMechanism struct. Either it was unreffed or "
+		           "possibly allocated on the stack. Always use gck_mechanism_new () and friends.");
+		return;
+	}
+
+	if (g_atomic_int_dec_and_test (refs)) {
+		g_free (mech->parameter);
+		g_dataset_id_remove_data (mech, mechanism_quark);
+		g_slice_free (GckMechanism, mech);
+	}
+}
diff --git a/gck/gck-module.c b/gck/gck-module.c
new file mode 100644
index 0000000..096a044
--- /dev/null
+++ b/gck/gck-module.c
@@ -0,0 +1,1228 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-module.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+#include "gck-marshal.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gck-module
+ * @title: GckModule
+ * @short_description: A loaded and initialized PKCS#11 module.
+ *
+ * A GckModule object holds a loaded PKCS#11 module. A PKCS#11 module is a shared library.
+ *
+ * You can load and initialize a PKCS#11 module with the gck_module_initialize() call. If you already
+ * have a loaded and initialized module that you'd like to use with the various gck functions, then
+ * you can use gck_module_new().
+ */
+
+/**
+ * GckModule:
+ *
+ * Holds a loaded and initialized PKCS#11 module.
+ */
+
+/**
+ * GckModuleInfo:
+ * @pkcs11_version_major: The major version of the module.
+ * @pkcs11_version_minor: The minor version of the module.
+ * @manufacturer_id: The module manufacturer.
+ * @flags: The module PKCS&num;11 flags.
+ * @library_description: The module description.
+ * @library_version_major: The major version of the library.
+ * @library_version_minor: The minor version of the library.
+ *
+ * Holds information about the PKCS&num;11 module.
+ *
+ * This structure corresponds to CK_MODULE_INFO in the PKCS#11 standard. The
+ * strings are NULL terminated for easier use.
+ *
+ * Use gck_module_info_free() to release this structure when done with it.
+ */
+
+/*
+ * MT safe
+ *
+ * The only thing that can change after object initialization in
+ * a GckModule is the finalized flag, which can be set
+ * to 1 in dispose.
+ */
+
+enum {
+	PROP_0,
+	PROP_PATH,
+	PROP_FUNCTIONS,
+	PROP_POOL_SESSIONS,
+	PROP_AUTO_AUTHENTICATE
+};
+
+enum {
+	AUTHENTICATE_SLOT,
+	AUTHENTICATE_OBJECT,
+	LAST_SIGNAL
+};
+
+typedef struct _GckModuleData {
+	GModule *module;
+	gchar *path;
+	gboolean initialized;
+	CK_FUNCTION_LIST_PTR funcs;
+	CK_C_INITIALIZE_ARGS init_args;
+} GckModuleData;
+
+typedef struct _GckModulePrivate {
+	GckModuleData data;
+	GStaticMutex mutex;
+	gboolean finalized;
+	GHashTable *open_sessions;
+	gint auto_authenticate;
+} GckModulePrivate;
+
+#define gck_module_GET_DATA(o) \
+      (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_MODULE, GckModuleData))
+
+G_DEFINE_TYPE (GckModule, gck_module, G_TYPE_OBJECT);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct _SessionPool {
+	CK_SLOT_ID slot;
+	CK_FUNCTION_LIST_PTR funcs;
+	GArray *ro_sessions; /* array of CK_SESSION_HANDLE */
+	GArray *rw_sessions; /* array of CK_SESSION_HANDLE */
+} SessionPool;
+
+/* ----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+static CK_RV
+create_mutex (void **mutex)
+{
+	if (!mutex)
+		return CKR_ARGUMENTS_BAD;
+
+	if (!g_thread_supported ()) {
+		g_warning ("cannot create pkcs11 mutex, threading has not been initialized");
+		return CKR_GENERAL_ERROR;
+	}
+
+	*mutex = g_mutex_new ();
+	g_return_val_if_fail (*mutex, CKR_GENERAL_ERROR);
+	return CKR_OK;
+}
+
+static CK_RV
+destroy_mutex (void *mutex)
+{
+	if (!mutex)
+		return CKR_MUTEX_BAD;
+	g_mutex_free ((GMutex*)mutex);
+	return CKR_OK;
+}
+
+static CK_RV
+lock_mutex (void *mutex)
+{
+	if (!mutex)
+		return CKR_MUTEX_BAD;
+	g_mutex_lock ((GMutex*)mutex);
+	return CKR_OK;
+}
+
+static CK_RV
+unlock_mutex (void *mutex)
+{
+	if (!mutex)
+		return CKR_MUTEX_BAD;
+	g_mutex_unlock ((GMutex*)mutex);
+	return CKR_OK;
+}
+
+static void
+close_session (CK_FUNCTION_LIST_PTR funcs, CK_SESSION_HANDLE handle)
+{
+	CK_RV rv;
+
+	g_return_if_fail (funcs);
+
+	rv = (funcs->C_CloseSession) (handle);
+	if (rv != CKR_OK) {
+		g_warning ("couldn't close session properly: %s",
+		           gck_message_from_rv (rv));
+	}
+}
+
+/* ----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static GckModulePrivate*
+lock_private (gpointer obj)
+{
+	GckModulePrivate *pv;
+	GckModule *self;
+
+	g_assert (GCK_IS_MODULE (obj));
+	self = GCK_MODULE (obj);
+
+	g_object_ref (self);
+
+	pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate);
+	g_static_mutex_lock (&pv->mutex);
+
+	return pv;
+}
+
+static void
+unlock_private (gpointer obj, GckModulePrivate *pv)
+{
+	GckModule *self;
+
+	g_assert (pv);
+	g_assert (GCK_IS_MODULE (obj));
+
+	self = GCK_MODULE (obj);
+
+	g_assert (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate) == pv);
+
+	g_static_mutex_unlock (&pv->mutex);
+	g_object_unref (self);
+}
+
+static void
+free_session_pool (gpointer p)
+{
+	SessionPool *pool = p;
+	guint i;
+
+	if (pool->ro_sessions) {
+		for(i = 0; i < pool->ro_sessions->len; ++i)
+			close_session (pool->funcs, g_array_index (pool->ro_sessions, CK_SESSION_HANDLE, i));
+		g_array_free (pool->ro_sessions, TRUE);
+	}
+
+	if (pool->rw_sessions) {
+		for(i = 0; i < pool->rw_sessions->len; ++i)
+			close_session (pool->funcs, g_array_index (pool->rw_sessions, CK_SESSION_HANDLE, i));
+		g_array_free (pool->rw_sessions, TRUE);
+	}
+
+	g_free (pool);
+}
+
+static gboolean
+push_session_table (GckModulePrivate *pv, CK_SLOT_ID slot, gulong flags, CK_SESSION_HANDLE handle)
+{
+	SessionPool *pool;
+	GArray *array;
+
+	g_assert (handle);
+
+	if (pv->open_sessions == NULL)
+		return FALSE;
+
+	pool = g_hash_table_lookup (pv->open_sessions, &slot);
+	if (!pool) {
+		pool = g_new0 (SessionPool, 1);
+		pool->funcs = pv->data.funcs;
+		g_hash_table_insert (pv->open_sessions, g_memdup (&slot, sizeof (slot)), pool);
+	}
+
+	if (flags & CKF_RW_SESSION) {
+		if (!pool->rw_sessions)
+			pool->rw_sessions = g_array_new (FALSE, TRUE, sizeof (CK_SESSION_HANDLE));
+		array = pool->rw_sessions;
+	} else {
+		if (!pool->ro_sessions)
+			pool->ro_sessions = g_array_new (FALSE, TRUE, sizeof (CK_SESSION_HANDLE));
+		array = pool->ro_sessions;
+	}
+
+	g_array_append_val (array, handle);
+	return TRUE;
+}
+
+static CK_SESSION_HANDLE
+pop_session_table (GckModulePrivate *pv, CK_SLOT_ID slot, gulong flags)
+{
+	CK_SESSION_HANDLE result = 0;
+	SessionPool *pool;
+	GArray **array;
+
+	g_return_val_if_fail (pv, 0);
+
+	if (!pv->open_sessions)
+		return 0;
+
+	pool = g_hash_table_lookup (pv->open_sessions, &slot);
+	if (pool == NULL)
+		return 0;
+
+	if (flags & CKF_RW_SESSION)
+		array = &pool->rw_sessions;
+	else
+		array = &pool->ro_sessions;
+
+	if (*array == NULL)
+		return 0;
+
+	g_assert ((*array)->len > 0);
+	result = g_array_index (*array, CK_SESSION_HANDLE, (*array)->len - 1);
+	g_assert (result != 0);
+	g_array_remove_index_fast (*array, (*array)->len - 1);
+
+	if (!(*array)->len) {
+		g_array_free (*array, TRUE);
+		*array = NULL;
+		if (!pool->rw_sessions && !pool->ro_sessions)
+			g_hash_table_remove (pv->open_sessions, &slot);
+	}
+
+	return result;
+}
+
+static void
+destroy_session_table (GckModulePrivate *pv)
+{
+	if (pv->open_sessions)
+		g_hash_table_unref (pv->open_sessions);
+	pv->open_sessions = NULL;
+}
+
+static void
+create_session_table (GckModulePrivate *pv)
+{
+	if (!pv->open_sessions)
+		pv->open_sessions = g_hash_table_new_full (_gck_ulong_hash, _gck_ulong_equal, g_free, free_session_pool);
+}
+
+CK_SESSION_HANDLE
+_gck_module_pooled_session_handle (GckModule *self, CK_SLOT_ID slot, gulong flags)
+{
+	GckModulePrivate *pv = lock_private (self);
+	CK_SESSION_HANDLE handle;
+
+	g_return_val_if_fail (GCK_IS_MODULE (self), 0);
+
+	{
+		handle = pop_session_table (pv, slot, flags);
+	}
+
+	unlock_private (self, pv);
+
+	return handle;
+}
+
+gboolean
+_gck_module_pool_session_handle (GckSession *session, CK_SESSION_HANDLE handle, GckModule *self)
+{
+	GckModuleData *data = gck_module_GET_DATA (self);
+	GckModulePrivate *pv;
+	CK_SESSION_INFO info;
+	gboolean handled = FALSE;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SESSION (session), FALSE);
+	g_return_val_if_fail (GCK_IS_MODULE (self), FALSE);
+
+	/* Get the session info so we know where to categorize this */
+	rv = (data->funcs->C_GetSessionInfo) (handle, &info);
+
+	if (rv == CKR_OK) {
+
+		pv = lock_private (self);
+
+		{
+			/* Keep this one around for later use */
+			handled = push_session_table (pv, info.slotID, info.flags, handle);
+		}
+
+		unlock_private (self, pv);
+
+	} else {
+
+		/* An already closed session, we don't want to bother with */
+		if (rv == CKR_SESSION_CLOSED || rv == CKR_SESSION_HANDLE_INVALID)
+			handled = TRUE;
+	}
+
+	return handled;
+}
+
+gboolean
+_gck_module_fire_authenticate_slot (GckModule *self, GckSlot *slot, gchar *label, gchar **password)
+{
+	GckTokenInfo *info;
+	gchar *allocated = NULL;
+	gboolean ret;
+
+	g_assert (GCK_IS_MODULE (self));
+
+	info = gck_slot_get_token_info (slot);
+	if (info != NULL) {
+
+		/*
+		 * We'll have tried to login at least once at this point,
+		 * with NULL password. This means that CKF_PROTECTED_AUTHENTICATION_PATH
+		 * tokens have had their chance and we don't need to prompt for it.
+		 */
+
+		if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+			return FALSE;
+
+		if (label == NULL)
+			label = allocated = g_strdup (info->label);
+
+		gck_token_info_free (info);
+	}
+
+	g_signal_emit (self, signals[AUTHENTICATE_SLOT], 0, slot, label, password, &ret);
+	g_free (allocated);
+	return ret;
+}
+
+gboolean
+_gck_module_fire_authenticate_object (GckModule *self, GckObject *object,
+                                      gchar *label, gchar **password)
+{
+	GckTokenInfo *info;
+	GckSlot *slot;
+	gboolean ret;
+
+	g_assert (GCK_IS_MODULE (self));
+	g_assert (GCK_IS_OBJECT (object));
+	g_assert (password);
+
+	slot = gck_object_get_slot (object);
+	info = gck_slot_get_token_info (slot);
+	g_object_unref (slot);
+
+	if (info != NULL) {
+		if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
+			gck_token_info_free (info);
+			*password = NULL;
+			return TRUE;
+		}
+
+		gck_token_info_free (info);
+	}
+
+	g_signal_emit (self, signals[AUTHENTICATE_OBJECT], 0, object, label, password, &ret);
+	return ret;
+}
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static gboolean
+gck_module_real_authenticate_slot (GckModule *module, GckSlot *self, gchar *label, gchar **password)
+{
+	return FALSE;
+}
+
+static gboolean
+gck_module_real_authenticate_object (GckModule *module, GckObject *object, gchar *label, gchar **password)
+{
+	return FALSE;
+}
+
+static void
+gck_module_init (GckModule *self)
+{
+	GckModulePrivate *pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate);
+	g_static_mutex_init (&pv->mutex);
+}
+
+static void
+gck_module_get_property (GObject *obj, guint prop_id, GValue *value,
+                          GParamSpec *pspec)
+{
+	GckModule *self = GCK_MODULE (obj);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, gck_module_get_path (self));
+		break;
+	case PROP_FUNCTIONS:
+		g_value_set_pointer (value, gck_module_get_functions (self));
+		break;
+	case PROP_AUTO_AUTHENTICATE:
+		g_value_set_int (value, gck_module_get_auto_authenticate (self));
+		break;
+	case PROP_POOL_SESSIONS:
+		g_value_set_boolean (value, gck_module_get_pool_sessions (self));
+		break;
+	}
+}
+
+static void
+gck_module_set_property (GObject *obj, guint prop_id, const GValue *value,
+                          GParamSpec *pspec)
+{
+	GckModule *self = GCK_MODULE (obj);
+	GckModuleData *data = gck_module_GET_DATA (obj);
+
+	/* Only allowed during initialization */
+	switch (prop_id) {
+	case PROP_PATH:
+		g_return_if_fail (!data->path);
+		data->path = g_value_dup_string (value);
+		break;
+	case PROP_FUNCTIONS:
+		g_return_if_fail (!data->funcs);
+		data->funcs = g_value_get_pointer (value);
+		break;
+	case PROP_AUTO_AUTHENTICATE:
+		gck_module_set_auto_authenticate (self, g_value_get_int (value));
+		break;
+	case PROP_POOL_SESSIONS:
+		gck_module_set_pool_sessions (self, g_value_get_boolean (value));
+		break;
+	}
+}
+
+static void
+gck_module_dispose (GObject *obj)
+{
+	GckModuleData *data = gck_module_GET_DATA (obj);
+	GckModulePrivate *pv = lock_private (obj);
+	gboolean finalize = FALSE;
+	CK_RV rv;
+
+	{
+		destroy_session_table (pv);
+
+		if (!pv->finalized && data->initialized && data->funcs) {
+			finalize = TRUE;
+			pv->finalized = TRUE;
+		}
+	}
+
+	unlock_private (obj, pv);
+
+	/* Must be careful when accessing funcs */
+	if (finalize) {
+		rv = (data->funcs->C_Finalize) (NULL);
+		if (rv != CKR_OK) {
+			g_warning ("C_Finalize on module '%s' failed: %s",
+			           data->path, gck_message_from_rv (rv));
+		}
+	}
+
+	G_OBJECT_CLASS (gck_module_parent_class)->dispose (obj);
+}
+
+static void
+gck_module_finalize (GObject *obj)
+{
+	GckModulePrivate *pv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GCK_TYPE_MODULE, GckModulePrivate);
+	GckModuleData *data = gck_module_GET_DATA (obj);
+
+	g_assert (!pv->open_sessions);
+
+	data->funcs = NULL;
+
+	if (data->module) {
+		if (!g_module_close (data->module))
+			g_warning ("failed to close the pkcs11 module: %s",
+			           g_module_error ());
+		data->module = NULL;
+	}
+
+	g_free (data->path);
+	data->path = NULL;
+
+	g_static_mutex_free (&pv->mutex);
+
+	G_OBJECT_CLASS (gck_module_parent_class)->finalize (obj);
+}
+
+
+static void
+gck_module_class_init (GckModuleClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	gck_module_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->get_property = gck_module_get_property;
+	gobject_class->set_property = gck_module_set_property;
+	gobject_class->dispose = gck_module_dispose;
+	gobject_class->finalize = gck_module_finalize;
+
+	klass->authenticate_object = gck_module_real_authenticate_object;
+	klass->authenticate_slot = gck_module_real_authenticate_slot;
+
+	/**
+	 * GckModule:path:
+	 *
+	 * The PKCS&num;11 module file path.
+	 *
+	 * This may be set to NULL if this object was created from an already
+	 * initialized module via the gck_module_new() function.
+	 */
+	g_object_class_install_property (gobject_class, PROP_PATH,
+		g_param_spec_string ("path", "Module Path", "Path to the PKCS11 Module",
+		                     NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckModule:functions:
+	 *
+	 * The raw PKCS&num;11 function list for the module.
+	 *
+	 * This points to a CK_FUNCTION_LIST structure.
+	 */
+	g_object_class_install_property (gobject_class, PROP_FUNCTIONS,
+		g_param_spec_pointer ("functions", "Function List", "PKCS11 Function List",
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckModule:auto-authenticate:
+	 *
+	 * Whether or not to automatically authenticate token objects that need
+	 * a C_Login call before they can be used.
+	 *
+	 * The #GckModule::authenticate-object signal will be fired when an
+	 * object needs to be authenticated.
+	 */
+	g_object_class_install_property (gobject_class, PROP_AUTO_AUTHENTICATE,
+		g_param_spec_int ("auto-authenticate", "Auto Authenticate", "Auto Login to Token when necessary",
+		                  0, G_MAXINT, 0, G_PARAM_READWRITE));
+
+	/**
+	 * GckModule:pool-sessions:
+	 *
+	 * Whether or not to pool PKCS&num;11 sessions. When this is set, sessions
+	 * will be pooled and reused if their flags match when gck_slot_open_session()
+	 * is called.
+	 */
+	g_object_class_install_property (gobject_class, PROP_POOL_SESSIONS,
+		g_param_spec_boolean ("pool-sessions", "Pool Sessions", "Pool sessions?",
+		                      FALSE, G_PARAM_READWRITE));
+
+	/**
+	 * GckModule::authenticate-slot:
+	 * @module: The module
+	 * @slot: The slot to be authenticated.
+	 * @string: A displayable label which describes the object.
+	 * @password: A gchar** where a password should be returned.
+	 *
+	 * This signal is emitted when a password is needed to authenticate a PKCS&num;11
+	 * slot. If the module prompts for passwords itself, then this signal will
+	 * not be emitted.
+	 *
+	 * Returns: FALSE if the user cancelled, TRUE if we should proceed.
+	 */
+	signals[AUTHENTICATE_SLOT] = g_signal_new ("authenticate-slot", GCK_TYPE_MODULE,
+			G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_slot),
+			g_signal_accumulator_true_handled, NULL, _gck_marshal_BOOLEAN__OBJECT_STRING_POINTER,
+			G_TYPE_BOOLEAN, 3, GCK_TYPE_SLOT, G_TYPE_STRING, G_TYPE_POINTER);
+
+	/**
+	 * GckModule::authenticate-object:
+	 * @module: The module.
+	 * @object: The object to be authenticated.
+	 * @label: A displayable label which describes the object.
+	 * @password: A gchar** where a password should be returned.
+	 *
+	 * This signal is emitted when a password is needed to authenticate a PKCS&num;11
+	 * object like a key. If the module prompts for passwords itself, then this signal will
+	 * not be emitted.
+	 *
+	 * Returns: FALSE if the user cancelled, TRUE if we should proceed.
+	 */
+	signals[AUTHENTICATE_OBJECT] = g_signal_new ("authenticate-object", GCK_TYPE_MODULE,
+			G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckModuleClass, authenticate_object),
+			g_signal_accumulator_true_handled, NULL, _gck_marshal_BOOLEAN__OBJECT_STRING_POINTER,
+			G_TYPE_BOOLEAN, 3, GCK_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_POINTER);
+
+	g_type_class_add_private (gobject_class, sizeof (GckModulePrivate));
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+/**
+ * gck_module_info_free:
+ * @module_info: The module info to free, or NULL.
+ *
+ * Free a GckModuleInfo structure.
+ **/
+void
+gck_module_info_free (GckModuleInfo *module_info)
+{
+	if (!module_info)
+		return;
+	g_free (module_info->library_description);
+	g_free (module_info->manufacturer_id);
+	g_free (module_info);
+}
+
+/**
+ * gck_module_initialize:
+ * @path: The file system path to the PKCS#11 module to load.
+ * @reserved: Extra arguments for the PKCS#11 module, should usually be NULL.
+ * @err: A location to store an error resulting from a failed load.
+ *
+ * Load and initialize a PKCS#11 module represented by a GckModule object.
+ *
+ * Return value: The loaded PKCS#11 module or NULL if failed.
+ **/
+GckModule*
+gck_module_initialize (const gchar *path, gpointer reserved, GError **err)
+{
+	CK_C_GetFunctionList get_function_list;
+	CK_FUNCTION_LIST_PTR funcs;
+	GckModuleData *data;
+	GModule *module;
+	GckModule *mod;
+	CK_RV rv;
+
+	g_return_val_if_fail (path != NULL, NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	/* Load the actual module */
+	module = g_module_open (path, 0);
+	if (!module) {
+		g_set_error (err, GCK_ERROR, (int)CKR_GCK_MODULE_PROBLEM,
+		             "Error loading pkcs11 module: %s", g_module_error ());
+		return NULL;
+	}
+
+	/* Get the entry point */
+	if (!g_module_symbol (module, "C_GetFunctionList", (void**)&get_function_list)) {
+		g_set_error (err, GCK_ERROR, (int)CKR_GCK_MODULE_PROBLEM,
+		             "Invalid pkcs11 module: %s", g_module_error ());
+		g_module_close (module);
+		return NULL;
+	}
+
+	/* Get the function list */
+	rv = (get_function_list) (&funcs);
+	if (rv != CKR_OK) {
+		g_set_error (err, GCK_ERROR, rv, "Couldn't get pkcs11 function list: %s",
+		             gck_message_from_rv (rv));
+		g_module_close (module);
+		return NULL;
+	}
+
+	mod = g_object_new (GCK_TYPE_MODULE, "functions", funcs, "path", path, NULL);
+	data = gck_module_GET_DATA (mod);
+	data->module = module;
+
+	memset (&data->init_args, 0, sizeof (data->init_args));
+	data->init_args.flags = CKF_OS_LOCKING_OK;
+	data->init_args.CreateMutex = create_mutex;
+	data->init_args.DestroyMutex = destroy_mutex;
+	data->init_args.LockMutex = lock_mutex;
+	data->init_args.UnlockMutex = unlock_mutex;
+	data->init_args.pReserved = reserved;
+
+	/* Now initialize the module */
+	rv = (data->funcs->C_Initialize) (&data->init_args);
+	if (rv != CKR_OK) {
+		g_set_error (err, GCK_ERROR, rv, "Couldn't initialize module: %s",
+		             gck_message_from_rv (rv));
+		g_object_unref (mod);
+		return NULL;
+	}
+
+	data->initialized = TRUE;
+	return mod;
+}
+
+/**
+ * gck_module_new:
+ * @funcs: Initialized PKCS#11 function list pointer
+ *
+ * Create a GckModule representing a PKCS#11 module. It is assumed that
+ * this the module is already initialized. In addition it will not be
+ * finalized when complete.
+ *
+ * Return value: The new PKCS#11 module.
+ **/
+GckModule*
+gck_module_new (CK_FUNCTION_LIST_PTR funcs)
+{
+	g_return_val_if_fail (funcs, NULL);
+	return g_object_new (GCK_TYPE_MODULE, "functions", funcs, NULL);
+}
+
+/**
+ * gck_module_equal:
+ * @module1: A pointer to the first GckModule
+ * @module2: A pointer to the second GckModule
+ *
+ * Checks equality of two modules. Two GckModule objects can point to the same
+ * underlying PKCS#11 module.
+ *
+ * Return value: TRUE if module1 and module2 are equal. FALSE if either is not a GckModule.
+ **/
+gboolean
+gck_module_equal (gconstpointer module1, gconstpointer module2)
+{
+	GckModuleData *data1, *data2;
+
+	if (module1 == module2)
+		return TRUE;
+	if (!GCK_IS_MODULE (module1) || !GCK_IS_MODULE (module2))
+		return FALSE;
+
+	data1 = gck_module_GET_DATA (module1);
+	data2 = gck_module_GET_DATA (module2);
+
+	return data1->funcs == data2->funcs;
+}
+
+/**
+ * gck_module_hash:
+ * @module: A pointer to a GckModule
+ *
+ * Create a hash value for the GckModule.
+ *
+ * This function is intended for easily hashing a GckModule to add to
+ * a GHashTable or similar data structure.
+ *
+ * Return value: An integer that can be used as a hash value, or 0 if invalid.
+ **/
+guint
+gck_module_hash (gconstpointer module)
+{
+	GckModuleData *data;
+
+	g_return_val_if_fail (GCK_IS_MODULE (module), 0);
+
+	data = gck_module_GET_DATA (module);
+
+	return g_direct_hash (data->funcs);
+}
+
+/**
+ * gck_module_get_info:
+ * @self: The module to get info for.
+ *
+ * Get the info about a PKCS#11 module.
+ *
+ * Return value: The module info. Release this with gck_module_info_free().
+ **/
+GckModuleInfo*
+gck_module_get_info (GckModule *self)
+{
+	GckModuleData *data = gck_module_GET_DATA (self);
+	GckModuleInfo *modinfo;
+	CK_INFO info;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
+	g_return_val_if_fail (data->funcs, NULL);
+
+	memset (&info, 0, sizeof (info));
+	rv = (data->funcs->C_GetInfo (&info));
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get module info: %s", gck_message_from_rv (rv));
+		return NULL;
+	}
+
+	modinfo = g_new0 (GckModuleInfo, 1);
+	modinfo->flags = info.flags;
+	modinfo->library_description = gck_string_from_chars (info.libraryDescription,
+	                                                       sizeof (info.libraryDescription));
+	modinfo->manufacturer_id = gck_string_from_chars (info.manufacturerID,
+	                                                   sizeof (info.manufacturerID));
+	modinfo->library_version_major = info.libraryVersion.major;
+	modinfo->library_version_minor = info.libraryVersion.minor;
+	modinfo->pkcs11_version_major = info.cryptokiVersion.major;
+	modinfo->pkcs11_version_minor = info.cryptokiVersion.minor;
+
+	return modinfo;
+}
+
+/**
+ * gck_module_get_slots:
+ * @self: The module for which to get the slots.
+ * @token_present: Whether to limit only to slots with a token present.
+ *
+ * Get the GckSlot objects for a given module.
+ *
+ * Return value: The possibly empty list of slots. Release this with gck_list_unref_free().
+ */
+GList*
+gck_module_get_slots (GckModule *self, gboolean token_present)
+{
+	GckModuleData *data = gck_module_GET_DATA (self);
+	CK_SLOT_ID_PTR slot_list;
+	CK_ULONG count, i;
+	GList *result;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
+	g_return_val_if_fail (data->funcs, NULL);
+
+	rv = (data->funcs->C_GetSlotList) (token_present ? CK_TRUE : CK_FALSE, NULL, &count);
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get slot count: %s", gck_message_from_rv (rv));
+		return NULL;
+	}
+
+	if (!count)
+		return NULL;
+
+	slot_list = g_new (CK_SLOT_ID, count);
+	rv = (data->funcs->C_GetSlotList) (token_present ? CK_TRUE : CK_FALSE, slot_list, &count);
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get slot list: %s", gck_message_from_rv (rv));
+		g_free (slot_list);
+		return NULL;
+	}
+
+	result = NULL;
+	for (i = 0; i < count; ++i) {
+		result = g_list_prepend (result, g_object_new (GCK_TYPE_SLOT,
+		                                               "handle", slot_list[i],
+		                                               "module", self, NULL));
+	}
+
+	g_free (slot_list);
+	return g_list_reverse (result);
+}
+
+/**
+ * gck_module_get_path:
+ * @self: The module for which to get the path.
+ *
+ * Get the file path of this module. This may not be an absolute path, and
+ * usually reflects the path passed to gck_module_initialize().
+ *
+ * Return value: The path, do not modify or free this value.
+ **/
+const gchar*
+gck_module_get_path (GckModule *self)
+{
+	GckModuleData *data = gck_module_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
+	return data->path;
+}
+
+/**
+ * gck_module_get_functions:
+ * @self: The module for which to get the function list.
+ *
+ * Get the PKCS#11 function list for the module.
+ *
+ * Return value: The function list, do not modify this structure.
+ **/
+CK_FUNCTION_LIST_PTR
+gck_module_get_functions (GckModule *self)
+{
+	GckModuleData *data = gck_module_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
+	return data->funcs;
+}
+
+
+/**
+ * gck_module_get_pool_sessions:
+ * @self: The module to get setting from.
+ *
+ * Get the reuse sessions setting. When this is set, sessions
+ * will be pooled and reused if their flags match when
+ * gck_slot_open_session() is called.
+ *
+ * Return value: Whether reusing sessions or not.
+ **/
+gboolean
+gck_module_get_pool_sessions (GckModule *self)
+{
+	GckModulePrivate *pv = lock_private (self);
+	gboolean ret;
+
+	g_return_val_if_fail (pv, FALSE);
+
+	{
+		ret = pv->open_sessions != NULL;
+	}
+
+	unlock_private (self, pv);
+
+	return ret;
+}
+
+/**
+ * gck_module_set_pool_sessions:
+ * @self: The module to set the setting on.
+ * @pool: Whether to reuse sessions or not.
+ *
+ * When this is set, sessions will be pooled and reused
+ * if their flags match when gck_slot_open_session() is called.
+ **/
+void
+gck_module_set_pool_sessions (GckModule *self, gboolean pool)
+{
+	GckModulePrivate *pv = lock_private (self);
+
+	g_return_if_fail (pv);
+
+	{
+		if (pool)
+			create_session_table (pv);
+		else
+			destroy_session_table (pv);
+	}
+
+	unlock_private (self, pv);
+	g_object_notify (G_OBJECT (self), "pool-sessions");
+}
+
+/**
+ * gck_module_get_auto_authenticate:
+ * @self: The module to get setting from.
+ *
+ * Get the auto login setting. When this is set, this slot
+ * will emit the 'authenticate-slot' signal when a session
+ * requires authentication, and the 'authenticate-object'
+ * signal when an object requires authintication.
+ *
+ * Return value: Whether auto login or not.
+ **/
+gint
+gck_module_get_auto_authenticate (GckModule *self)
+{
+	GckModulePrivate *pv = lock_private (self);
+	gint ret;
+
+	g_return_val_if_fail (pv, FALSE);
+
+	{
+		ret = pv->auto_authenticate;
+	}
+
+	unlock_private (self, pv);
+
+	return ret;
+}
+
+/**
+ * gck_module_set_auto_authenticate:
+ * @self: The module to set the setting on.
+ * @auto_authenticate: Whether auto login or not.
+ *
+ * When this is set, this slot
+ * will emit the 'authenticate-slot' signal when a session
+ * requires authentication, and the 'authenticate-object'
+ * signal when an object requires authintication.
+ **/
+void
+gck_module_set_auto_authenticate (GckModule *self, gint auto_authenticate)
+{
+	GckModulePrivate *pv = lock_private (self);
+
+	/* HACK: Get needed fix around API freeze. */
+	if (auto_authenticate == 1)
+		auto_authenticate = GCK_AUTHENTICATE_TOKENS | GCK_AUTHENTICATE_OBJECTS;
+
+	g_return_if_fail (pv);
+
+	{
+		pv->auto_authenticate = auto_authenticate;
+	}
+
+	unlock_private (self, pv);
+	g_object_notify (G_OBJECT (self), "auto-authenticate");
+}
+
+/**
+ * gck_module_enumerate_objects:
+ * @self: The module to enumerate objects.
+ * @func: Function to call for each object.
+ * @user_data: Data to pass to the function.
+ * @...: The arguments must be triples of: attribute type, data type, value.
+ *
+ * Call a function for every matching object on the module. This call may
+ * block for an indefinite period.
+ *
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG,
+ * 				GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with GCK_INVALID.</para>
+ *
+ * This function will open a session per slot. It's recommended that you
+ * set the 'reuse-sessions' property on each slot if you'll be calling
+ * it a lot.
+ *
+ * You can access the session in which the object was found, by using the
+ * gck_object_get_session() function on the resulting objects.
+ *
+ * This function skips tokens that are not initialize, and makes a best effort to
+ * find objects on valid tokens.
+ *
+ * The function can return FALSE to stop the enumeration.
+ *
+ * Return value: If FALSE then an error prevented all matching objects from being enumerated.
+ **/
+gboolean
+gck_module_enumerate_objects (GckModule *self, GckObjectForeachFunc func,
+                               gpointer user_data, ...)
+{
+	GckAttributes *attrs;
+	GError *error = NULL;
+	va_list va;
+
+	va_start (va, user_data);
+	attrs = gck_attributes_new_valist (g_realloc, va);
+	va_end (va);
+
+	gck_module_enumerate_objects_full (self, attrs, NULL, func, user_data, &error);
+	gck_attributes_unref (attrs);
+
+	if (error != NULL) {
+		g_warning ("enumerating objects failed: %s", error->message);
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/**
+ * gck_module_enumerate_objects_full:
+ * @self: The module to enumerate objects.
+ * @attrs: Attributes that the objects must have, or empty for all objects.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @func: Function to call for each object.
+ * @user_data: Data to pass to the function.
+ * @error: Location to return error information.
+ *
+ * Call a function for every matching object on the module. This call may
+ * block for an indefinite period.
+ *
+ * This function will open a session per slot. It's recommended that you
+ * set the 'reuse-sessions' property on each slot if you'll be calling
+ * it a lot.
+ *
+ * You can access the session in which the object was found, by using the
+ * gck_object_get_session() function on the resulting objects.
+ *
+ * The function can return FALSE to stop the enumeration.
+ *
+ * Return value: If FALSE then an error prevented all matching objects from being enumerated.
+ **/
+gboolean
+gck_module_enumerate_objects_full (GckModule *self, GckAttributes *attrs,
+                                    GCancellable *cancellable, GckObjectForeachFunc func,
+                                    gpointer user_data, GError **err)
+{
+	gboolean stop = FALSE;
+	gboolean ret = TRUE;
+	GList *objects, *o;
+	GList *slots, *l;
+	GError *error = NULL;
+	GckSession *session;
+
+	g_return_val_if_fail (GCK_IS_MODULE (self), FALSE);
+	g_return_val_if_fail (attrs, FALSE);
+	g_return_val_if_fail (func, FALSE);
+
+	gck_attributes_ref (attrs);
+	slots = gck_module_get_slots (self, TRUE);
+
+	for (l = slots; ret && !stop && l; l = g_list_next (l)) {
+
+		/* TODO: We really should allow the caller to specify the flags, at least read-write */
+		session = gck_slot_open_session (l->data, CKF_RW_SESSION | CKF_SERIAL_SESSION, &error);
+		if (!session) {
+			g_return_val_if_fail (error != NULL, FALSE);
+
+			/* Ignore these errors when enumerating */
+			if (g_error_matches (error, GCK_ERROR, CKR_USER_PIN_NOT_INITIALIZED)) {
+				g_clear_error (&error);
+
+			} else {
+				ret = FALSE;
+				g_propagate_error (err, error);
+				error = NULL;
+			}
+			continue;
+		}
+
+		objects = gck_session_find_objects_full (session, attrs, cancellable, &error);
+		if (error) {
+			ret = FALSE;
+			g_object_unref (session);
+			g_propagate_error (err, error);
+			error = NULL;
+			continue;
+		}
+
+		for (o = objects; !stop && o; o = g_list_next (o)) {
+			gck_object_set_session (o->data, session);
+			if (!(func)(o->data, user_data)) {
+				stop = TRUE;
+				break;
+			}
+		}
+
+		g_object_unref (session);
+		gck_list_unref_free (objects);
+	}
+
+	gck_list_unref_free (slots);
+	gck_attributes_unref (attrs);
+
+	return ret;
+}
+
+/**
+ * GckObjectForeachFunc:
+ * @object: The enumerated object.
+ * @user_data: Data passed to enumerate function.
+ *
+ * This function is passed to gck_module_enumerate_objects() or a similar function.
+ * It is called once for each object matched.
+ *
+ * The GckSession through which the object is accessible can be retrieved by calling
+ * gck_object_get_session() on object.
+ *
+ * Returns: TRUE to continue enumerating, FALSE to stop.
+ */
diff --git a/gck/gck-object.c b/gck/gck-object.c
new file mode 100644
index 0000000..bc3e8ad
--- /dev/null
+++ b/gck/gck-object.c
@@ -0,0 +1,1615 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-object.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gck-object
+ * @title: GckObject
+ * @short_description: Represents a PKCS11 object such as a key or certificate.
+ *
+ * A GckObject holds a handle to a PKCS11 object such as a key or certificate. Token objects
+ * are stored on the token persistently. Others are transient and are called session objects.
+ */
+
+/**
+ * GckObject:
+ *
+ * Represents a PKCS11 object handle such as a key or certifiacte.
+ */
+
+/*
+ * MT safe -- Nothing in GckObjectData changes between
+ * init and finalize. All GckObjectPrivate access between init
+ * and finalize is locked.
+ */
+
+enum {
+	PROP_0,
+	PROP_MODULE,
+	PROP_SLOT,
+	PROP_HANDLE,
+	PROP_SESSION
+};
+
+typedef struct _GckObjectData {
+	GckModule *module;
+	GckSlot *slot;
+	CK_OBJECT_HANDLE handle;
+} GckObjectData;
+
+typedef struct _GckObjectPrivate {
+	GckObjectData data;
+	GStaticMutex mutex;
+	GckSession *session;
+} GckObjectPrivate;
+
+#define GCK_OBJECT_GET_DATA(o) \
+      (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_OBJECT, GckObjectData))
+
+G_DEFINE_TYPE (GckObject, gck_object, G_TYPE_OBJECT);
+
+/* ----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+run_call_with_session (GckCall *call, GckSession *session)
+{
+	g_assert (GCK_IS_CALL (call));
+	g_assert (GCK_IS_SESSION (session));
+
+	/* Hold onto this session for the length of the call */
+	g_object_set_data_full (G_OBJECT (call), "call-opened-session",
+	                        g_object_ref (session), g_object_unref);
+
+	_gck_call_async_object (call, session);
+	_gck_call_async_go (call);
+}
+
+static void
+opened_session (GObject *obj, GAsyncResult *result, gpointer user_data)
+{
+	GckSession *session;
+	GError *err = NULL;
+	GckCall *call;
+
+	g_assert (GCK_IS_CALL (user_data));
+	call = GCK_CALL (user_data);
+
+	session = gck_slot_open_session_finish (GCK_SLOT (obj), result, &err);
+
+	/* Transtfer the error to the outer call and finish */
+	if (!session) {
+		_gck_call_async_short (user_data, err->code);
+		g_error_free (err);
+		return;
+	}
+
+	run_call_with_session (GCK_CALL (user_data), session);
+	g_object_unref (session);
+}
+
+static void
+require_session_async (GckObject *self, GckCall *call,
+                       gulong flags, GCancellable *cancellable)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	GckSession *session;
+
+	g_assert (GCK_IS_OBJECT (self));
+
+	session = gck_object_get_session (self);
+	if (session) {
+		run_call_with_session (call, session);
+		g_object_unref (session);
+	} else {
+		gck_slot_open_session_async (data->slot, flags, NULL, NULL,
+		                              cancellable, opened_session, call);
+	}
+
+}
+
+static GckSession*
+require_session_sync (GckObject *self, gulong flags, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	GckSession *session;
+
+	g_assert (GCK_IS_OBJECT (self));
+
+	session = gck_object_get_session (self);
+	if (session)
+		return session;
+
+	return gck_slot_open_session (data->slot, flags, err);
+}
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_object_init (GckObject *self)
+{
+	GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE(self, GCK_TYPE_OBJECT, GckObjectPrivate));
+	g_static_mutex_init (&pv->mutex);
+}
+
+static void
+gck_object_get_property (GObject *obj, guint prop_id, GValue *value,
+                          GParamSpec *pspec)
+{
+	GckObject *self = GCK_OBJECT (obj);
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_value_take_object (value, gck_object_get_module (self));
+		break;
+	case PROP_SLOT:
+		g_value_take_object (value, gck_object_get_slot (self));
+		break;
+	case PROP_SESSION:
+		g_value_take_object (value, gck_object_get_session (self));
+		break;
+	case PROP_HANDLE:
+		g_value_set_ulong (value, gck_object_get_handle (self));
+		break;
+	}
+}
+
+static void
+gck_object_set_property (GObject *obj, guint prop_id, const GValue *value,
+                          GParamSpec *pspec)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (obj);
+	GckObject *self = GCK_OBJECT (obj);
+
+	/* The sets to data below are only allowed during construction */
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_return_if_fail (!data->module);
+		data->module = g_value_get_object (value);
+		g_return_if_fail (data->module);
+		g_object_ref (data->module);
+		break;
+	case PROP_SLOT:
+		g_return_if_fail (!data->slot);
+		data->slot = g_value_get_object (value);
+		g_return_if_fail (data->slot);
+		g_object_ref (data->slot);
+		break;
+	case PROP_SESSION:
+		gck_object_set_session (self, g_value_get_object (value));
+		break;
+	case PROP_HANDLE:
+		g_return_if_fail (!data->handle);
+		data->handle = g_value_get_ulong (value);
+		break;
+	}
+}
+
+static void
+gck_object_finalize (GObject *obj)
+{
+	GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE(obj, GCK_TYPE_OBJECT, GckObjectPrivate));
+	GckObjectData *data = GCK_OBJECT_GET_DATA (obj);
+
+	if (data->slot)
+		g_object_unref (data->slot);
+	data->slot = NULL;
+
+	if (data->module)
+		g_object_unref (data->module);
+	data->module = NULL;
+
+	if (pv->session)
+		g_object_unref (pv->session);
+	pv->session = NULL;
+
+	data->handle = 0;
+
+	g_static_mutex_free (&pv->mutex);
+
+	G_OBJECT_CLASS (gck_object_parent_class)->finalize (obj);
+}
+
+
+static void
+gck_object_class_init (GckObjectClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	gck_object_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->get_property = gck_object_get_property;
+	gobject_class->set_property = gck_object_set_property;
+	gobject_class->finalize = gck_object_finalize;
+
+	/**
+	 * GckObject:module:
+	 *
+	 * The GckModule that this object belongs to.
+	 */
+	g_object_class_install_property (gobject_class, PROP_MODULE,
+		g_param_spec_object ("module", "Module", "PKCS11 Module",
+		                     GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckObject:slot:
+	 *
+	 * The GckSlot that this object belongs to.
+	 *
+	 * If this is a token object then it will be stored on the token in this slot.
+	 * If this is a session object, then it belongs to a session opened on this slot.
+	 */
+	g_object_class_install_property (gobject_class, PROP_SLOT,
+		g_param_spec_object ("slot", "slot", "PKCS11 Slot",
+		                     GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckObject:handle:
+	 *
+	 * The raw PKCS11 handle for this object.
+	 */
+	g_object_class_install_property (gobject_class, PROP_HANDLE,
+		g_param_spec_ulong ("handle", "Object Handle", "PKCS11 Object Handle",
+		                   0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckObject:session:
+	 *
+	 * The PKCS11 session to make calls on when this object needs to
+	 * perform operations on itself.
+	 *
+	 * If this is NULL then a new session is opened for each operation,
+	 * such as gck_object_get(), gck_object_set() or gck_object_destroy().
+	 */
+	g_object_class_install_property (gobject_class, PROP_SESSION,
+		g_param_spec_object ("session", "session", "PKCS11 Session to make calls on",
+		                     GCK_TYPE_SESSION, G_PARAM_READWRITE));
+
+	g_type_class_add_private (klass, sizeof (GckObjectPrivate));
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+/**
+ * gck_object_from_handle:
+ * @slot: The slot on which this object is present.
+ * @handle: The raw handle of the object.
+ *
+ * Initialize a GckObject from a raw PKCS#11 handle. Normally you would use
+ * gck_session_create_object() or gck_session_find_objects() to access objects.
+ *
+ * Return value: The new GckObject. You should use g_object_unref() when done with this object.
+ **/
+GckObject*
+gck_object_from_handle (GckSlot *slot, CK_OBJECT_HANDLE handle)
+{
+	GckModule *module = NULL;
+	GckObject *object;
+
+	g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
+
+	module = gck_slot_get_module (slot);
+	object = g_object_new (GCK_TYPE_OBJECT, "module", module, "handle", handle, "slot", slot, NULL);
+	g_object_unref (module);
+
+	return object;
+}
+
+/**
+ * gck_objects_from_handle_array:
+ * @slot: The slot on which these objects are present.
+ * @handles: The raw object handles.
+ * @n_handles: The number of raw object handles.
+ *
+ * Initialize a list of GckObject from raw PKCS#11 handles. The handles argument must contain
+ * contiguous CK_OBJECT_HANDLE handles in an array.
+ *
+ * Return value: The list of GckObject. You should use gck_list_unref_free() when done with
+ * this list.
+ **/
+GList*
+gck_objects_from_handle_array (GckSlot *slot, CK_OBJECT_HANDLE_PTR handles, CK_ULONG n_handles)
+{
+	GList *results = NULL;
+	CK_ULONG i;
+
+	g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
+	g_return_val_if_fail (handles || !n_handles, NULL);
+
+	for (i = 0; i < n_handles; ++i)
+		results = g_list_prepend (results, gck_object_from_handle (slot, handles[i]));
+	return g_list_reverse (results);
+}
+
+/**
+ * gck_object_equal:
+ * @object1: A pointer to the first GckObject
+ * @object2: A pointer to the second GckObject
+ *
+ * Checks equality of two objects. Two GckObject objects can point to the same
+ * underlying PKCS#11 object.
+ *
+ * Return value: TRUE if object1 and object2 are equal. FALSE if either is not a GckObject.
+ **/
+gboolean
+gck_object_equal (gconstpointer object1, gconstpointer object2)
+{
+	GckObjectData *data1, *data2;
+
+	if (object1 == object2)
+		return TRUE;
+	if (!GCK_IS_OBJECT (object1) || !GCK_IS_OBJECT (object2))
+		return FALSE;
+
+	data1 = GCK_OBJECT_GET_DATA (object1);
+	data2 = GCK_OBJECT_GET_DATA (object2);
+
+	return data1->handle == data2->handle &&
+	       gck_slot_equal (data1->slot, data2->slot);
+}
+
+/**
+ * gck_object_hash:
+ * @object: A pointer to a GckObject
+ *
+ * Create a hash value for the GckObject.
+ *
+ * This function is intended for easily hashing a GckObject to add to
+ * a GHashTable or similar data structure.
+ *
+ * Return value: An integer that can be used as a hash value, or 0 if invalid.
+ **/
+guint
+gck_object_hash (gconstpointer object)
+{
+	GckObjectData *data;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (object), 0);
+
+	data = GCK_OBJECT_GET_DATA (object);
+
+	return _gck_ulong_hash (&data->handle) ^
+	       gck_slot_hash (data->slot);
+}
+
+
+/**
+ * gck_object_get_handle:
+ * @self: The object.
+ *
+ * Get the raw PKCS#11 handle of a GckObject.
+ *
+ * Return value: The raw object handle.
+ **/
+CK_OBJECT_HANDLE
+gck_object_get_handle (GckObject *self)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_OBJECT (self), (CK_OBJECT_HANDLE)-1);
+	return data->handle;
+}
+
+/**
+ * gck_object_get_module:
+ * @self: The object.
+ *
+ * Get the PKCS#11 module to which this object belongs.
+ *
+ * Return value: The module, which should be unreffed after use.
+ **/
+GckModule*
+gck_object_get_module (GckObject *self)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL);
+	return g_object_ref (data->module);
+}
+
+/**
+ * gck_object_get_slot:
+ * @self: The object.
+ *
+ * Get the PKCS#11 slot to which this object belongs.
+ *
+ * Return value: The slot, which should be unreffed after use.
+ **/
+GckSlot*
+gck_object_get_slot (GckObject *self)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (GCK_IS_SLOT (data->slot), NULL);
+	return g_object_ref (data->slot);
+}
+
+/**
+ * gck_object_get_session:
+ * @self: The object
+ *
+ * Get the PKCS#11 session assigned to make calls on when operating
+ * on this object.
+ *
+ * This will only return a session if it was set explitly on this
+ * object. By default an object will open and close sessions
+ * appropriate for its calls.
+ *
+ * Return value: The assigned session, which must be unreffed after use.
+ **/
+GckSession*
+gck_object_get_session (GckObject *self)
+{
+	GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_OBJECT, GckObjectPrivate));
+	GckSession *session;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+
+	g_static_mutex_lock (&pv->mutex);
+
+	{
+		session = pv->session ? g_object_ref (pv->session) : NULL;
+	}
+
+	g_static_mutex_unlock (&pv->mutex);
+
+	return session;
+}
+
+/**
+ * gck_object_set_session:
+ * @self: The object
+ * @session: The assigned session
+ *
+ * Set the PKCS#11 session assigned to make calls on when operating
+ * on this object.
+ *
+ * It isn't always necessary to assign a session to an object.
+ * By default an object will open and close sessions appropriate for
+ * its calls.
+ *
+ * If you assign a read-only session, then calls on this object
+ * that modify the state of the object will probably fail.
+ **/
+void
+gck_object_set_session (GckObject *self, GckSession *session)
+{
+	GckObjectPrivate *pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_OBJECT, GckObjectPrivate));
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+
+	g_static_mutex_lock (&pv->mutex);
+
+	{
+		if (session)
+			g_object_ref (session);
+		if (pv->session)
+			g_object_unref (pv->session);
+		pv->session = session;
+	}
+
+	g_static_mutex_unlock (&pv->mutex);
+}
+
+/* --------------------------------------------------------------------------------------
+ * DESTROY
+ */
+
+typedef struct _Destroy {
+	GckArguments base;
+	CK_OBJECT_HANDLE object;
+} Destroy;
+
+static CK_RV
+perform_destroy (Destroy *args)
+{
+	g_assert (args);
+	return (args->base.pkcs11->C_DestroyObject) (args->base.handle, args->object);
+}
+
+/**
+ * gck_object_destroy:
+ * @self: The object to destroy.
+ * @err: A location to return an error.
+ *
+ * Destroy a PKCS#11 object, deleting it from storage or the session.
+ * This call may block for an indefinite period.
+ *
+ * Return value: Whether the call was successful or not.
+ **/
+gboolean
+gck_object_destroy (GckObject *self, GError **err)
+{
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+	return gck_object_destroy_full (self, NULL, err);
+}
+
+/**
+ * gck_object_destroy_full:
+ * @self: The object to destroy.
+ * @cancellable: Optional cancellable object, or NULL to ignore.
+ * @err: A location to return an error.
+ *
+ * Destroy a PKCS#11 object, deleting it from storage or the session.
+ * This call may block for an indefinite period.
+ *
+ * Return value: Whether the call was successful or not.
+ **/
+gboolean
+gck_object_destroy_full (GckObject *self, GCancellable *cancellable, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	Destroy args = { GCK_ARGUMENTS_INIT, 0 };
+	GckSession *session;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (GCK_IS_SLOT (data->slot), FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+
+	args.object = data->handle;
+
+	session = require_session_sync (self, CKF_RW_SESSION, err);
+	if (session)
+		ret = _gck_call_sync (session, perform_destroy, NULL, &args, cancellable, err);
+	g_object_unref (session);
+	return ret;
+}
+
+/**
+ * gck_object_destroy_async:
+ * @self: The object to destroy.
+ * @cancellable: Optional cancellable object, or NULL to ignore.
+ * @callback: Callback which is called when operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Destroy a PKCS#11 object, deleting it from storage or the session.
+ * This call will return immediately and complete asynchronously.
+ **/
+void
+gck_object_destroy_async (GckObject *self, GCancellable *cancellable,
+                           GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	Destroy* args;
+	GckCall *call;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+	g_return_if_fail (GCK_IS_SLOT (data->slot));
+
+	args = _gck_call_async_prep (data->slot, self, perform_destroy, NULL, sizeof (*args), NULL);
+	args->object = data->handle;
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	require_session_async (self, call, CKF_RW_SESSION, cancellable);
+}
+
+/**
+ * gck_object_destroy_finish:
+ * @self: The object being destroyed.
+ * @result: The result of the destory operation passed to the callback.
+ * @err: A location to store an error.
+ *
+ * Get the status of the operation to destroy a PKCS#11 object, begun with
+ * gck_object_destroy_async().
+ *
+ * Return value: Whether the object was destroyed successfully or not.
+ */
+gboolean
+gck_object_destroy_finish (GckObject *self, GAsyncResult *result, GError **err)
+{
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (GCK_IS_CALL (result), FALSE);
+	return _gck_call_basic_finish (result, err);
+}
+
+/* --------------------------------------------------------------------------------------
+ * SET ATTRIBUTES
+ */
+
+typedef struct _SetAttributes {
+	GckArguments base;
+	GckAttributes *attrs;
+	CK_OBJECT_HANDLE object;
+} SetAttributes;
+
+static CK_RV
+perform_set_attributes (SetAttributes *args)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	CK_ULONG n_attrs;
+
+	g_assert (args);
+	attrs = _gck_attributes_commit_out (args->attrs, &n_attrs);
+
+	return (args->base.pkcs11->C_SetAttributeValue) (args->base.handle, args->object,
+	                                                 attrs, n_attrs);
+}
+
+static void
+free_set_attributes (SetAttributes *args)
+{
+	g_assert (args);
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+/**
+ * gck_object_set:
+ * @self: The object to set attributes on.
+ * @err: A location to return an error.
+ * @...: The attributes to set.
+ *
+ * Set PKCS#11 attributes on an object.
+ * This call may block for an indefinite period.
+ *
+ * The arguments must be triples of: attribute type, data type, value
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG,
+ * 				GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with GCK_INVALID.</para>
+ *
+ * Return value: Whether the call was successful or not.
+ **/
+gboolean
+gck_object_set (GckObject *self, GError **err, ...)
+{
+	GckAttributes *attrs;
+	va_list va;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+
+	va_start (va, err);
+	attrs = gck_attributes_new_valist (g_realloc, va);
+	va_end (va);
+
+	rv = gck_object_set_full (self, attrs, NULL, err);
+
+	gck_attributes_unref (attrs);
+	return rv;
+}
+
+/**
+ * gck_object_set_full:
+ * @self: The object to set attributes on.
+ * @attrs: The attributes to set on the object.
+ * @cancellable: Optional cancellable object, or NULL to ignore.
+ * @err: A location to return an error.
+ *
+ * Set PKCS#11 attributes on an object. This call may block for an indefinite period.
+ *
+ * Return value: Whether the call was successful or not.
+ **/
+gboolean
+gck_object_set_full (GckObject *self, GckAttributes *attrs,
+                      GCancellable *cancellable, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	SetAttributes args;
+	GckSession *session;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (attrs, FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+
+	_gck_attributes_lock (attrs);
+
+	memset (&args, 0, sizeof (args));
+	args.attrs = attrs;
+	args.object = data->handle;
+
+	session = require_session_sync (self, CKF_RW_SESSION, err);
+	if (session)
+		ret = _gck_call_sync (session, perform_set_attributes, NULL, &args, cancellable, err);
+
+	_gck_attributes_unlock (attrs);
+	g_object_unref (session);
+	return ret;
+}
+
+/**
+ * gck_object_set_async:
+ * @self: The object to set attributes on.
+ * @attrs: The attributes to set on the object.
+ * @cancellable: Optional cancellable object, or NULL to ignore.
+ * @callback: Callback which is called when operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Set PKCS#11 attributes on an object. This call will return
+ * immediately and completes asynchronously.
+ **/
+void
+gck_object_set_async (GckObject *self, GckAttributes *attrs, GCancellable *cancellable,
+                       GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	SetAttributes *args;
+	GckCall *call;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+	g_return_if_fail (attrs);
+
+	args = _gck_call_async_prep (data->slot, self, perform_set_attributes,
+	                              NULL, sizeof (*args), free_set_attributes);
+
+	_gck_attributes_lock (attrs);
+	args->attrs = gck_attributes_ref (attrs);
+	args->object = data->handle;
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	require_session_async (self, call, CKF_RW_SESSION, cancellable);
+}
+
+/**
+ * gck_object_set_finish:
+ * @self: The object to set attributes on.
+ * @result: The result of the destory operation passed to the callback.
+ * @err: A location to store an error.
+ *
+ * Get the status of the operation to set attributes on a PKCS#11 object,
+ * begun with gck_object_set_async().
+ *
+ * Return value: Whether the attributes were successfully set on the object or not.
+ */
+gboolean
+gck_object_set_finish (GckObject *self, GAsyncResult *result, GError **err)
+{
+	SetAttributes *args;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (GCK_IS_CALL (result), FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+
+	/* Unlock the attributes we were using */
+	args = _gck_call_arguments (result, SetAttributes);
+	g_assert (args->attrs);
+	_gck_attributes_unlock (args->attrs);
+
+	return _gck_call_basic_finish (result, err);
+}
+
+/* ------------------------------------------------------------------------------------
+ * GET ATTRIBUTES
+ */
+
+typedef struct _GetAttributes {
+	GckArguments base;
+	CK_OBJECT_HANDLE object;
+	GckAttributes *attrs;
+} GetAttributes;
+
+/*
+ * Certain failure return values only apply to individual attributes
+ * being retrieved. These are ignored, since the attribute should
+ * already have -1 set as the length.
+ */
+static gboolean
+is_ok_get_attributes_rv (CK_RV rv)
+{
+	switch (rv) {
+	case CKR_OK:
+	case CKR_ATTRIBUTE_SENSITIVE:
+	case CKR_ATTRIBUTE_TYPE_INVALID:
+		return TRUE;
+	default:
+		return FALSE;
+	}
+}
+
+static CK_RV
+perform_get_attributes (GetAttributes *args)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	CK_ULONG n_attrs;
+	CK_RV rv;
+
+	g_assert (args);
+	g_assert (args->attrs);
+
+	/* Prepare all the attributes */
+	attrs = _gck_attributes_prepare_in (args->attrs, &n_attrs);
+
+	/* Get the size of each value */
+	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object,
+	                                               attrs, n_attrs);
+	if (!is_ok_get_attributes_rv (rv))
+		return rv;
+
+	/* Allocate memory for each value */
+	attrs = _gck_attributes_commit_in (args->attrs, &n_attrs);
+
+	/* Now get the actual values */
+	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object,
+	                                               attrs, n_attrs);
+
+	if (is_ok_get_attributes_rv (rv))
+		rv = CKR_OK;
+
+	return rv;
+}
+
+static void
+free_get_attributes (GetAttributes *args)
+{
+	g_assert (args);
+	g_assert (args->attrs);
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+
+/**
+ * gck_object_get:
+ * @self: The object to get attributes from.
+ * @err: A location to store an error.
+ * @...: The attribute types to get.
+ *
+ * Get the specified attributes from the object. This call may
+ * block for an indefinite period.
+ *
+ * Return value: The resulting PKCS#11 attributes, or NULL if an error occurred.
+ * The result must be unreffed when you're finished with it.
+ **/
+GckAttributes*
+gck_object_get (GckObject *self, GError **err, ...)
+{
+	GckAttributes *attrs;
+	va_list va;
+	gulong type;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	attrs = gck_attributes_new ();
+	va_start (va, err);
+	for (;;) {
+		type = va_arg (va, gulong);
+		if (type == GCK_INVALID)
+			break;
+		gck_attributes_add_invalid (attrs, type);
+	}
+	va_end (va);
+
+	if (!gck_object_get_full (self, attrs, NULL, err)) {
+		gck_attributes_unref (attrs);
+		return NULL;
+	}
+
+	return attrs;
+}
+
+/**
+ * gck_object_get_full:
+ * @self: The object to get attributes from.
+ * @attrs: The attributes to get, with the types filled in.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to store an error.
+ *
+ * Get the specified attributes from the object. This call may
+ * block for an indefinite period.
+ *
+ * No extra references are added to the returned attributes pointer.
+ * During this call you may not access the attributes in any way.
+ *
+ * Return value: A pointer to the filled in attributes if successful,
+ * or NULL if not.
+ **/
+GckAttributes*
+gck_object_get_full (GckObject *self, GckAttributes *attrs,
+                      GCancellable *cancellable, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	GetAttributes args;
+	GckSession *session;
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (attrs, NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	session = require_session_sync (self, 0, err);
+	if (!session)
+		return NULL;
+
+	_gck_attributes_lock (attrs);
+
+	memset (&args, 0, sizeof (args));
+	args.attrs = attrs;
+	args.object = data->handle;
+
+	ret = _gck_call_sync (session, perform_get_attributes, NULL, &args, cancellable, err);
+	_gck_attributes_unlock (attrs);
+	g_object_unref (session);
+
+	return ret ? attrs : NULL;
+}
+
+/**
+ * gck_object_get_async:
+ * @self: The object to get attributes from.
+ * @attrs: The attributes to get, initialized with their types.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: A callback which is called when the operation completes.
+ * @user_data: Data to be passed to the callback.
+ *
+ * Get the specified attributes from the object. The attributes will be cleared
+ * of their current values, and new attributes will be stored. The attributes
+ * should not be accessed in any way except for referencing and unreferencing
+ * them until gck_object_get_finish() is called.
+ *
+ * This call returns immediately and completes asynchronously.
+ **/
+void
+gck_object_get_async (GckObject *self, GckAttributes *attrs, GCancellable *cancellable,
+                       GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	GetAttributes *args;
+	GckCall *call;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+	g_return_if_fail (attrs);
+
+	args = _gck_call_async_prep (data->slot, self, perform_get_attributes,
+	                              NULL, sizeof (*args), free_get_attributes);
+
+	_gck_attributes_lock (attrs);
+	args->attrs = gck_attributes_ref (attrs);
+	args->object = data->handle;
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	require_session_async (self, call, 0, cancellable);
+}
+
+/**
+ * gck_object_get_finish:
+ * @self: The object to get attributes from.
+ * @result: The result passed to the callback.
+ * @err: A location to store an error.
+ *
+ * Get the result of a get operation and return specified attributes from
+ * the object.
+ *
+ * No extra references are added to the returned attributes pointer.
+ *
+ * Return value: The filled in attributes structure if successful or
+ * NULL if not successful.
+ **/
+GckAttributes*
+gck_object_get_finish (GckObject *self, GAsyncResult *result, GError **err)
+{
+	GetAttributes *args;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (GCK_IS_CALL (result), NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	args = _gck_call_arguments (result, GetAttributes);
+	_gck_attributes_unlock (args->attrs);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+
+	return args->attrs;
+}
+
+/* ---------------------------------------------------------------------------------
+ * GET ATTRIBUTE DATA
+ */
+
+typedef struct _GetAttributeData {
+	GckArguments base;
+	CK_OBJECT_HANDLE object;
+	CK_ATTRIBUTE_TYPE type;
+	GckAllocator allocator;
+	guchar *result;
+	gsize n_result;
+} GetAttributeData;
+
+static CK_RV
+perform_get_attribute_data (GetAttributeData *args)
+{
+	CK_ATTRIBUTE attr;
+	CK_RV rv;
+
+	g_assert (args);
+	g_assert (args->allocator);
+
+	attr.type = args->type;
+	attr.ulValueLen = 0;
+	attr.pValue = 0;
+
+	/* Get the size of the value */
+	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object,
+	                                               &attr, 1);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* Allocate memory for the value */
+	args->result = (args->allocator) (NULL, attr.ulValueLen + 1);
+	g_assert (args->result);
+	attr.pValue = args->result;
+
+	/* Now get the actual value */
+	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object,
+	                                               &attr, 1);
+
+	if (rv == CKR_OK) {
+		args->n_result = attr.ulValueLen;
+		args->result[args->n_result] = 0;
+	}
+
+	return rv;
+}
+
+static void
+free_get_attribute_data (GetAttributeData *args)
+{
+	g_assert (args);
+	g_free (args->result);
+	g_free (args);
+}
+
+/**
+ * gck_object_get_data:
+ * @self: The object to get attribute data from.
+ * @attr_type: The attribute to get data for.
+ * @n_data: The length of the resulting data.
+ * @err: A location to store an error.
+ *
+ * Get the data for the specified attribute from the object. For convenience
+ * the returned data has a null terminator.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: The resulting PKCS#11 attribute data, or NULL if an error occurred.
+ **/
+gpointer
+gck_object_get_data (GckObject *self, gulong attr_type, gsize *n_data, GError **err)
+{
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (n_data, NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	return gck_object_get_data_full (self, attr_type, g_realloc, NULL, n_data, err);
+}
+
+/**
+ * gck_object_get_data_full:
+ * @self: The object to get attribute data from.
+ * @attr_type: The attribute to get data for.
+ * @allocator: An allocator with which to allocate memory for the data, or NULL for default.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @n_data: The length of the resulting data.
+ * @err: A location to store an error.
+ *
+ * Get the data for the specified attribute from the object. For convenience
+ * the returned data has an extra null terminator, not included in the returned length.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: The resulting PKCS#11 attribute data, or NULL if an error occurred.
+ **/
+gpointer
+gck_object_get_data_full (GckObject *self, gulong attr_type, GckAllocator allocator,
+                           GCancellable *cancellable, gsize *n_data, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	GetAttributeData args;
+	GckSession *session;
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (n_data, NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	if (!allocator)
+		allocator = g_realloc;
+
+	session = require_session_sync (self, 0, err);
+	if (!session)
+		return NULL;
+
+	memset (&args, 0, sizeof (args));
+	args.allocator = allocator;
+	args.object = data->handle;
+	args.type = attr_type;
+
+	ret = _gck_call_sync (session, perform_get_attribute_data, NULL, &args, cancellable, err);
+	g_object_unref (session);
+
+	/* Free any value if failed */
+	if (!ret) {
+		if (args.result)
+			(allocator) (args.result, 0);
+		return NULL;
+	}
+
+	*n_data = args.n_result;
+	return args.result;
+}
+
+/**
+ * gck_object_get_data_async:
+ * @self: The object to get attribute data from.
+ * @attr_type: The attribute to get data for.
+ * @allocator: An allocator with which to allocate memory for the data, or NULL for default.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to be passed to the callback.
+ *
+ * Get the data for the specified attribute from the object.
+ *
+ * This call will return immediately and complete asynchronously.
+ **/
+void
+gck_object_get_data_async (GckObject *self, gulong attr_type, GckAllocator allocator,
+                            GCancellable *cancellable, GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	GetAttributeData *args;
+	GckCall *call;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+
+	if (!allocator)
+		allocator = g_realloc;
+
+	args = _gck_call_async_prep (data->slot, self, perform_get_attribute_data,
+	                              NULL, sizeof (*args), free_get_attribute_data);
+
+	args->allocator = allocator;
+	args->object = data->handle;
+	args->type = attr_type;
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	require_session_async (self, call, 0, cancellable);
+}
+
+/**
+ * gck_object_get_data_finish:
+ * @self: The object to get an attribute from.
+ * @result: The result passed to the callback.
+ * @n_data: The length of the resulting data.
+ * @err: A location to store an error.
+ *
+ * Get the result of an operation to get attribute data from
+ * an object. For convenience the returned data has an extra null terminator,
+ * not included in the returned length.
+ *
+ *
+ * Return value: The PKCS#11 attribute data or NULL if an error occurred.
+ **/
+gpointer
+gck_object_get_data_finish (GckObject *self, GAsyncResult *result,
+                             gsize *n_data, GError **err)
+{
+	GetAttributeData *args;
+	guchar *data;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (GCK_IS_CALL (result), NULL);
+	g_return_val_if_fail (n_data, NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+
+	args = _gck_call_arguments (result, GetAttributeData);
+
+	*n_data = args->n_result;
+	data = args->result;
+	args->result = NULL;
+
+	return data;
+}
+
+/* ---------------------------------------------------------------------------------------
+ * SET TEMPLATE
+ */
+
+typedef struct _set_template_args {
+	GckArguments base;
+	CK_OBJECT_HANDLE object;
+	CK_ATTRIBUTE_TYPE type;
+	GckAttributes *attrs;
+} set_template_args;
+
+static CK_RV
+perform_set_template (set_template_args *args)
+{
+	CK_ATTRIBUTE attr;
+	CK_ULONG n_attrs;
+
+	g_assert (args);
+
+	attr.type = args->type;
+	attr.pValue = _gck_attributes_commit_out (args->attrs, &n_attrs);
+	attr.ulValueLen = n_attrs * sizeof (CK_ATTRIBUTE);
+
+	return (args->base.pkcs11->C_SetAttributeValue) (args->base.handle, args->object, &attr, 1);
+}
+
+static void
+free_set_template (set_template_args *args)
+{
+	g_assert (args);
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+/**
+ * gck_object_set_template:
+ * @self: The object to set an attribute template on.
+ * @attr_type: The attribute template type.
+ * @attrs: The attribute template.
+ * @err: A location to store an error.
+ *
+ * Set an attribute template on the object. The attr_type must be for
+ * an attribute which contains a template.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: TRUE if the operation succeeded.
+ **/
+gboolean
+gck_object_set_template (GckObject *self, gulong attr_type, GckAttributes *attrs,
+                          GError **err)
+{
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+	return gck_object_set_template_full (self, attr_type, attrs, NULL, err);
+}
+
+/**
+ * gck_object_set_template_full:
+ * @self: The object to set an attribute template on.
+ * @attr_type: The attribute template type.
+ * @attrs: The attribute template.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to store an error.
+ *
+ * Set an attribute template on the object. The attr_type must be for
+ * an attribute which contains a template.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: TRUE if the operation succeeded.
+ **/
+gboolean
+gck_object_set_template_full (GckObject *self, gulong attr_type, GckAttributes *attrs,
+                               GCancellable *cancellable, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	set_template_args args;
+	GckSession *session;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (attrs, FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+
+	_gck_attributes_lock (attrs);
+
+	memset (&args, 0, sizeof (args));
+	args.attrs = attrs;
+	args.type = attr_type;
+	args.object = data->handle;
+
+	session = require_session_sync (self, CKF_RW_SESSION, err);
+	if (session)
+		ret = _gck_call_sync (session, perform_set_template, NULL, &args, cancellable, err);
+
+	_gck_attributes_unlock (attrs);
+	g_object_unref (session);
+	return ret;
+}
+
+/**
+ * gck_object_set_template_async:
+ * @self: The object to set an attribute template on.
+ * @attr_type: The attribute template type.
+ * @attrs: The attribute template.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to be passed to the callback.
+ *
+ * Set an attribute template on the object. The attr_type must be for
+ * an attribute which contains a template.
+ *
+ * This call will return immediately and complete asynchronously.
+ **/
+void
+gck_object_set_template_async (GckObject *self, gulong attr_type, GckAttributes *attrs,
+                                GCancellable *cancellable, GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	set_template_args *args;
+	GckCall *call;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+	g_return_if_fail (attrs);
+
+	args = _gck_call_async_prep (data->slot, self, perform_set_template,
+	                              NULL, sizeof (*args), free_set_template);
+
+	_gck_attributes_lock (attrs);
+	args->attrs = gck_attributes_ref (attrs);
+	args->type = attr_type;
+	args->object = data->handle;
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	require_session_async (self, call, CKF_RW_SESSION, cancellable);
+}
+
+/**
+ * gck_object_set_template_finish:
+ * @self: The object to set an attribute template on.
+ * @result: The result passed to the callback.
+ * @err: A location to store an error.
+ *
+ * Get the result of an operation to set attribute template on
+ * an object.
+ *
+ * Return value: TRUE if the operation succeeded.
+ **/
+gboolean
+gck_object_set_template_finish (GckObject *self, GAsyncResult *result, GError **err)
+{
+	set_template_args *args;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (GCK_IS_CALL (result), FALSE);
+	g_return_val_if_fail (!err || !*err, FALSE);
+
+	/* Unlock the attributes we were using */
+	args = _gck_call_arguments (result, set_template_args);
+	g_assert (args->attrs);
+	_gck_attributes_unlock (args->attrs);
+
+	return _gck_call_basic_finish (result, err);
+}
+
+/* ---------------------------------------------------------------------------------------
+ * GET TEMPLATE
+ */
+
+typedef struct _get_template_args {
+	GckArguments base;
+	CK_OBJECT_HANDLE object;
+	CK_ATTRIBUTE_TYPE type;
+	GckAttributes *attrs;
+} get_template_args;
+
+static CK_RV
+perform_get_template (get_template_args *args)
+{
+	CK_ATTRIBUTE attr;
+	CK_ULONG n_attrs, i;
+	CK_RV rv;
+
+	g_assert (args);
+	g_assert (!args->attrs);
+
+	args->attrs = gck_attributes_new ();
+	attr.type = args->type;
+	attr.ulValueLen = 0;
+	attr.pValue = 0;
+
+	/* Get the length of the entire template */
+	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, &attr, 1);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* Number of attributes, rounded down */
+	n_attrs = (attr.ulValueLen / sizeof (CK_ATTRIBUTE));
+	for (i = 0; i < n_attrs; ++i)
+		gck_attributes_add_empty (args->attrs, 0);
+
+	/* Prepare all the attributes */
+	_gck_attributes_lock (args->attrs);
+	attr.pValue = _gck_attributes_prepare_in (args->attrs, &n_attrs);
+
+	/* Get the size of each value */
+	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, &attr, 1);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* Allocate memory for each value */
+	attr.pValue = _gck_attributes_commit_in (args->attrs, &n_attrs);
+
+	/* Now get the actual values */
+	return (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object, &attr, 1);
+}
+
+static void
+free_get_template (get_template_args *args)
+{
+	g_assert (args);
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+/**
+ * gck_object_get_template:
+ * @self: The object to get an attribute template from.
+ * @attr_type: The attribute template type.
+ * @err: A location to store an error.
+ *
+ * Get an attribute template from the object. The attr_type must be for
+ * an attribute which returns a template.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: The resulting PKCS#11 attribute template, or NULL if an error occurred.
+ **/
+GckAttributes*
+gck_object_get_template (GckObject *self, gulong attr_type, GError **err)
+{
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	return gck_object_get_template_full (self, attr_type, NULL, err);
+}
+
+/**
+ * gck_object_get_template_full:
+ * @self: The object to get an attribute template from.
+ * @attr_type: The template attribute type.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to store an error.
+ *
+ * Get an attribute template from the object. The attr_type must be for
+ * an attribute which returns a template.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: The resulting PKCS#11 attribute template, or NULL if an error occurred.
+ **/
+GckAttributes*
+gck_object_get_template_full (GckObject *self, gulong attr_type,
+                               GCancellable *cancellable, GError **err)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	get_template_args args;
+	GckSession *session;
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	session = require_session_sync (self, 0, err);
+	if (!session)
+		return NULL;
+
+	memset (&args, 0, sizeof (args));
+	args.object = data->handle;
+	args.type = attr_type;
+
+	ret = _gck_call_sync (session, perform_get_template, NULL, &args, cancellable, err);
+	g_object_unref (session);
+
+	_gck_attributes_unlock (args.attrs);
+
+	/* Free any value if failed */
+	if (!ret) {
+		gck_attributes_unref (args.attrs);
+		args.attrs = NULL;
+	}
+
+	return args.attrs;
+}
+
+/**
+ * gck_object_get_template_async:
+ * @self: The object to get an attribute template from.
+ * @attr_type: The template attribute type.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to be passed to the callback.
+ *
+ * Get an attribute template from the object. The attr_type must be for
+ * an attribute which returns a template.
+ *
+ * This call will return immediately and complete asynchronously.
+ **/
+void
+gck_object_get_template_async (GckObject *self, gulong attr_type,
+                                GCancellable *cancellable, GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+	GckObjectData *data = GCK_OBJECT_GET_DATA (self);
+	get_template_args *args;
+	GckCall *call;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+
+	args = _gck_call_async_prep (data->slot, self, perform_get_template,
+	                              NULL, sizeof (*args), free_get_template);
+
+	args->object = data->handle;
+	args->type = attr_type;
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	require_session_async (self, call, 0, cancellable);
+}
+
+/**
+ * gck_object_get_template_finish:
+ * @self: The object to get an attribute from.
+ * @result: The result passed to the callback.
+ * @err: A location to store an error.
+ *
+ * Get the result of an operation to get attribute template from
+ * an object.
+ *
+ * Return value: The resulting PKCS#11 attribute template, or NULL if an error occurred.
+ **/
+GckAttributes*
+gck_object_get_template_finish (GckObject *self, GAsyncResult *result,
+                                GError **err)
+{
+	get_template_args *args;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), NULL);
+	g_return_val_if_fail (GCK_IS_CALL (result), NULL);
+	g_return_val_if_fail (!err || !*err, NULL);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+
+	args = _gck_call_arguments (result, get_template_args);
+	_gck_attributes_unlock (args->attrs);
+	return gck_attributes_ref (args->attrs);
+}
diff --git a/gck/gck-private.h b/gck/gck-private.h
new file mode 100644
index 0000000..f30f21c
--- /dev/null
+++ b/gck/gck-private.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-private.h - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#ifndef GCK_PRIVATE_H_
+#define GCK_PRIVATE_H_
+
+#include "gck.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+/* ---------------------------------------------------------------------------
+ * ATTRIBUTE INTERNALS
+ */
+
+void                _gck_attributes_lock                   (GckAttributes *attrs);
+
+void                _gck_attributes_unlock                 (GckAttributes *attrs);
+
+CK_ATTRIBUTE_PTR    _gck_attributes_prepare_in             (GckAttributes *attrs,
+                                                             CK_ULONG_PTR n_attrs);
+
+CK_ATTRIBUTE_PTR    _gck_attributes_commit_in              (GckAttributes *attrs,
+                                                             CK_ULONG_PTR n_attrs);
+
+CK_ATTRIBUTE_PTR    _gck_attributes_commit_out             (GckAttributes *attrs,
+                                                             CK_ULONG_PTR n_attrs);
+
+/* ----------------------------------------------------------------------------
+ * MISC
+ */
+
+guint               _gck_ulong_hash                        (gconstpointer v);
+
+gboolean            _gck_ulong_equal                       (gconstpointer v1,
+                                                             gconstpointer v2);
+
+/* ----------------------------------------------------------------------------
+ * MODULE
+ */
+
+gboolean            _gck_module_fire_authenticate_slot     (GckModule *module,
+                                                             GckSlot *slot,
+                                                             gchar *label,
+                                                             gchar **password);
+
+gboolean            _gck_module_fire_authenticate_object   (GckModule *module,
+                                                             GckObject *object,
+                                                             gchar *label,
+                                                             gchar **password);
+
+gboolean            _gck_module_pool_session_handle        (GckSession *session,
+                                                             CK_SESSION_HANDLE handle,
+                                                             GckModule *self);
+
+CK_SESSION_HANDLE   _gck_module_pooled_session_handle      (GckModule *module,
+                                                             CK_SLOT_ID slot,
+                                                             gulong flags);
+
+/* ----------------------------------------------------------------------------
+ * SLOT
+ */
+
+GckObject*         _gck_slot_object_from_handle           (GckSlot *slot,
+                                                             CK_OBJECT_HANDLE handle);
+
+/* ----------------------------------------------------------------------------
+ * CALL
+ */
+
+typedef CK_RV (*GckPerformFunc) (gpointer call_data);
+typedef gboolean (*GckCompleteFunc) (gpointer call_data, CK_RV result);
+
+typedef struct _GckCall GckCall;
+
+typedef struct _GckArguments {
+	GckCall *call;
+
+	/* For the call function to use */
+	CK_FUNCTION_LIST_PTR pkcs11;
+	CK_ULONG handle;
+
+} GckArguments;
+
+#define GCK_ARGUMENTS_INIT 	   { NULL, NULL, 0 }
+
+#define GCK_TYPE_CALL             (_gck_call_get_type())
+#define GCK_CALL(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_CALL, GckCall))
+#define GCK_CALL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_CALL, GckCall))
+#define GCK_IS_CALL(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_CALL))
+#define GCK_IS_CALL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_CALL))
+#define GCK_CALL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_CALL, GckCallClass))
+
+typedef struct _GckCallClass GckCallClass;
+
+GType              _gck_call_get_type                    (void) G_GNUC_CONST;
+
+#define            _gck_call_arguments(call, type)       (type*)(_gck_call_get_arguments (GCK_CALL (call)))
+
+gpointer           _gck_call_get_arguments               (GckCall *call);
+
+void               _gck_call_uninitialize                (void);
+
+gboolean           _gck_call_sync                        (gpointer object,
+                                                           gpointer perform,
+                                                           gpointer complete,
+                                                           gpointer args,
+                                                           GCancellable *cancellable,
+                                                           GError **err);
+
+gpointer           _gck_call_async_prep                  (gpointer object,
+                                                           gpointer cb_object,
+                                                           gpointer perform,
+                                                           gpointer complete,
+                                                           gsize args_size,
+                                                           gpointer destroy_func);
+
+GckCall*          _gck_call_async_ready                 (gpointer args,
+                                                           GCancellable *cancellable,
+                                                           GAsyncReadyCallback callback,
+                                                           gpointer user_data);
+
+void               _gck_call_async_go                    (GckCall *call);
+
+void               _gck_call_async_ready_go              (gpointer args,
+                                                           GCancellable *cancellable,
+                                                           GAsyncReadyCallback callback,
+                                                           gpointer user_data);
+
+void               _gck_call_async_short                 (GckCall *call,
+                                                           CK_RV rv);
+
+gboolean           _gck_call_basic_finish                (GAsyncResult *result,
+                                                           GError **err);
+
+void               _gck_call_async_object                (GckCall *call,
+                                                           gpointer object);
+
+#endif /* GCK_PRIVATE_H_ */
diff --git a/gck/gck-session.c b/gck/gck-session.c
new file mode 100644
index 0000000..dc34f2f
--- /dev/null
+++ b/gck/gck-session.c
@@ -0,0 +1,2886 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-session.h - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-marshal.h"
+#include "gck-private.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+/**
+ * SECTION:gck-session
+ * @title: GckSession
+ * @short_description: Represents an open PKCS11 session.
+ *
+ * Before performing any PKCS11 operations, a session must be opened. This is
+ * analogous to an open database handle, or a file handle.
+ */
+
+/**
+ * GckSession:
+ *
+ * Represents an open PKCS11 session.
+ */
+
+/**
+ * GckMechanism:
+ * @type: The mechanism type
+ * @parameter: Mechanism specific data.
+ * @n_parameter: Length of mechanism specific data.
+ *
+ * Represents a mechanism used with crypto operations.
+ */
+
+enum {
+	DISCARD_HANDLE,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_MODULE,
+	PROP_HANDLE,
+	PROP_SLOT
+};
+
+typedef struct _GckSessionData {
+	GckSlot *slot;
+	GckModule *module;
+	CK_SESSION_HANDLE handle;
+} GckSessionData;
+
+typedef struct _GckSessionPrivate {
+
+	/* Remain same from init to finalize */
+	GckSessionData data;
+
+	/* Modified atomically */
+	gint discarded;
+	gint auto_login;
+
+} GckSessionPrivate;
+
+#define GCK_SESSION_GET_DATA(o) \
+      (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_SESSION, GckSessionData))
+
+G_DEFINE_TYPE (GckSession, gck_session, G_TYPE_OBJECT);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* ----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+static GckSessionPrivate*
+lock_private (gpointer obj)
+{
+	GckSessionPrivate *pv;
+	GckSession *self;
+
+	g_return_val_if_fail (GCK_IS_SESSION (obj), NULL);
+	self = GCK_SESSION (obj);
+
+	g_object_ref (self);
+
+	pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SESSION, GckSessionPrivate);
+	/* g_static_mutex_lock (&pv->mutex); */
+
+	return pv;
+}
+
+static void
+unlock_private (gpointer obj, GckSessionPrivate *pv)
+{
+	GckSession *self;
+
+	g_assert (pv);
+	g_assert (GCK_IS_SESSION (obj));
+
+	self = GCK_SESSION (obj);
+
+	g_assert (G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SESSION, GckSessionPrivate) == pv);
+
+	/* g_static_mutex_unlock (&pv->mutex); */
+	g_object_unref (self);
+}
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static gboolean
+gck_session_real_discard_handle (GckSession *self, CK_OBJECT_HANDLE handle)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	CK_FUNCTION_LIST_PTR funcs;
+	CK_RV rv;
+
+	/* The default functionality, close the handle */
+
+	g_return_val_if_fail (data->module, FALSE);
+	g_object_ref (data->module);
+
+	funcs = gck_module_get_functions (data->module);
+	g_return_val_if_fail (funcs, FALSE);
+
+	rv = (funcs->C_CloseSession) (handle);
+	if (rv != CKR_OK) {
+		g_warning ("couldn't close session properly: %s",
+		           gck_message_from_rv (rv));
+	}
+
+	g_object_unref (data->module);
+	return TRUE;
+}
+
+static void
+gck_session_init (GckSession *self)
+{
+
+}
+
+static void
+gck_session_get_property (GObject *obj, guint prop_id, GValue *value,
+                           GParamSpec *pspec)
+{
+	GckSession *self = GCK_SESSION (obj);
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_value_take_object (value, gck_session_get_module (self));
+		break;
+	case PROP_HANDLE:
+		g_value_set_ulong (value, gck_session_get_handle (self));
+		break;
+	case PROP_SLOT:
+		g_value_take_object (value, gck_session_get_slot (self));
+		break;
+	}
+}
+
+static void
+gck_session_set_property (GObject *obj, guint prop_id, const GValue *value,
+                           GParamSpec *pspec)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (obj);
+
+	/* Only valid calls are from constructor */
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_return_if_fail (!data->module);
+		data->module = g_value_dup_object (value);
+		g_return_if_fail (data->module);
+		break;
+	case PROP_HANDLE:
+		g_return_if_fail (!data->handle);
+		data->handle = g_value_get_ulong (value);
+		break;
+	case PROP_SLOT:
+		g_return_if_fail (!data->slot);
+		data->slot = g_value_dup_object (value);
+		g_return_if_fail (data->slot);
+		break;
+	}
+}
+
+static void
+gck_session_dispose (GObject *obj)
+{
+	GckSessionPrivate *pv;
+	GckSession *self = GCK_SESSION (obj);
+	gboolean handled;
+	gint discarded;
+
+	g_return_if_fail (GCK_IS_SESSION (self));
+
+	pv = lock_private (obj);
+
+	{
+		discarded = g_atomic_int_get (&pv->discarded);
+		if (!discarded && g_atomic_int_compare_and_exchange (&pv->discarded, discarded, 1)) {
+
+			/*
+			 * Let the world know that we're discarding the session
+			 * handle. This allows session reuse to work.
+			 */
+
+			g_signal_emit_by_name (self, "discard-handle", pv->data.handle, &handled);
+			g_return_if_fail (handled);
+		}
+
+	}
+
+	unlock_private (obj, pv);
+
+	G_OBJECT_CLASS (gck_session_parent_class)->dispose (obj);
+}
+
+static void
+gck_session_finalize (GObject *obj)
+{
+	GckSessionPrivate *pv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GCK_TYPE_SESSION, GckSessionPrivate);
+	GckSessionData *data = GCK_SESSION_GET_DATA (obj);
+
+	g_assert (pv->discarded != 0);
+
+	if (data->slot)
+		g_object_unref (data->slot);
+	data->slot = NULL;
+
+	if (data->module)
+		g_object_unref (data->module);
+	data->module = NULL;
+
+	G_OBJECT_CLASS (gck_session_parent_class)->finalize (obj);
+}
+
+static void
+gck_session_class_init (GckSessionClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	gck_session_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->get_property = gck_session_get_property;
+	gobject_class->set_property = gck_session_set_property;
+	gobject_class->dispose = gck_session_dispose;
+	gobject_class->finalize = gck_session_finalize;
+
+	klass->discard_handle = gck_session_real_discard_handle;
+
+	/**
+	 * GckSession:module:
+	 *
+	 * The GckModule that this session is opened on.
+	 */
+	g_object_class_install_property (gobject_class, PROP_MODULE,
+		g_param_spec_object ("module", "Module", "PKCS11 Module",
+		                     GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckSession:handle:
+	 *
+	 * The raw CK_SESSION_HANDLE handle of this session.
+	 */
+	g_object_class_install_property (gobject_class, PROP_HANDLE,
+		g_param_spec_ulong ("handle", "Session Handle", "PKCS11 Session Handle",
+		                    0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckSession:slot:
+	 *
+	 * The GckSlot this session is opened on.
+	 */
+	g_object_class_install_property (gobject_class, PROP_SLOT,
+		g_param_spec_object ("slot", "Slot that this session uses", "PKCS11 Slot",
+		                     GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckSession::discard-handle:
+	 * @session: The session.
+	 * @handle: The handle being discarded.
+	 *
+	 * When a GckSession is being disposed of it emits this signal to allow
+	 * a session pool to pick up the handle and keep it around.
+	 *
+	 * If no signal handler claims the handle, then it is closed. This is used by
+	 * gck_module_set_pool_sessions() to implement the module session pool.
+	 *
+	 * Returns: Whether or not this handle was claimed.
+	 */
+	signals[DISCARD_HANDLE] = g_signal_new ("discard-handle", GCK_TYPE_SESSION,
+	                G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GckSessionClass, discard_handle),
+			g_signal_accumulator_true_handled, NULL,
+			_gck_marshal_BOOLEAN__ULONG, G_TYPE_BOOLEAN, 1, G_TYPE_ULONG);
+
+	g_type_class_add_private (klass, sizeof (GckSessionPrivate));
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+/**
+ * GckSessionInfo:
+ * @slot_id: The handle of the PKCS11 slot that this session is opened on.
+ * @state: The user login state of the session.
+ * @flags: Various PKCS11 flags.
+ * @device_error: The last device error that occurred from an operation on this session.
+ *
+ * Information about the session. This is analogous to a CK_SESSION_INFO structure.
+ *
+ * When done with this structure, release it using gck_session_info_free().
+ */
+
+/**
+ * gck_session_info_free:
+ * @session_info: Session info to free.
+ *
+ * Free the GckSessionInfo structure and all associated memory.
+ **/
+void
+gck_session_info_free (GckSessionInfo *session_info)
+{
+	if (!session_info)
+		return;
+	g_free (session_info);
+}
+
+/**
+ * gck_session_from_handle:
+ * @slot: The slot which the session belongs to.
+ * @handle: The raw PKCS#11 handle of the session.
+ *
+ * Initialize a GckSession object from a raw PKCS#11 session handle.
+ * Usually one would use the gck_slot_open_session() function to
+ * create a session.
+ *
+ * Return value: The new GckSession object.
+ **/
+GckSession*
+gck_session_from_handle (GckSlot *slot, CK_SESSION_HANDLE handle)
+{
+	GckModule *module;
+	GckSession *session;
+
+	g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
+
+	module = gck_slot_get_module (slot);
+	session = g_object_new (GCK_TYPE_SESSION, "module", module,
+	                        "handle", handle, "slot", slot, NULL);
+	g_object_unref (module);
+
+	return session;
+}
+
+/**
+ * gck_session_get_handle:
+ * @self: The session object.
+ *
+ * Get the raw PKCS#11 session handle from a GckSession object.
+ *
+ * Return value: The raw session handle.
+ **/
+CK_SESSION_HANDLE
+gck_session_get_handle (GckSession *self)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_SESSION (self), (CK_SESSION_HANDLE)-1);
+	return data->handle;
+}
+
+/**
+ * gck_session_get_module:
+ * @self: The session object.
+ *
+ * Get the PKCS#11 module to which this session belongs.
+ *
+ * Return value: The module, which should be unreffed after use.
+ **/
+GckModule*
+gck_session_get_module (GckSession *self)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL);
+	return g_object_ref (data->module);
+}
+
+/**
+ * gck_session_get_slot:
+ * @self: The session object.
+ *
+ * Get the PKCS#11 slot to which this session belongs.
+ *
+ * Return value: The slot, which should be unreffed after use.
+ **/
+GckSlot*
+gck_session_get_slot (GckSession *self)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+	g_return_val_if_fail (GCK_IS_SLOT (data->slot), NULL);
+	return g_object_ref (data->slot);
+}
+
+/**
+ * gck_session_get_info:
+ * @self: The session object.
+ *
+ * Get information about the session.
+ *
+ * Return value: The session info. Use the gck_session_info_free() to release
+ * when done.
+ **/
+GckSessionInfo*
+gck_session_get_info (GckSession *self)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	GckSessionInfo *sessioninfo;
+	CK_FUNCTION_LIST_PTR funcs;
+	CK_SESSION_INFO info;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL);
+
+	g_object_ref (data->module);
+
+	funcs = gck_module_get_functions (data->module);
+	g_return_val_if_fail (funcs, NULL);
+
+	memset (&info, 0, sizeof (info));
+	rv = (funcs->C_GetSessionInfo) (data->handle, &info);
+
+	g_object_unref (data->module);
+
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get session info: %s", gck_message_from_rv (rv));
+		return NULL;
+	}
+
+	sessioninfo = g_new0 (GckSessionInfo, 1);
+	sessioninfo->flags = info.flags;
+	sessioninfo->slot_id = info.slotID;
+	sessioninfo->state = info.state;
+	sessioninfo->device_error = info.ulDeviceError;
+
+	return sessioninfo;
+}
+
+/* ---------------------------------------------------------------------------------------------
+ * INIT PIN
+ */
+
+typedef struct _InitPin {
+	GckArguments base;
+	guchar *pin;
+	gsize n_pin;
+} InitPin;
+
+
+static void
+free_init_pin (InitPin *args)
+{
+	g_free (args->pin);
+	g_free (args);
+}
+
+static CK_RV
+perform_init_pin (InitPin *args)
+{
+	return (args->base.pkcs11->C_InitPIN) (args->base.handle, (CK_BYTE_PTR)args->pin,
+	                                       args->n_pin);
+}
+
+/**
+ * gck_session_init_pin:
+ * @self: Initialize PIN for this session's slot.
+ * @pin: The user's PIN, or NULL for protected authentication path.
+ * @n_pin: The length of the PIN.
+ * @err: A location to return an error.
+ *
+ * Initialize the user's pin on this slot that this session is opened on.
+ * According to the PKCS#11 standards, the session must be logged in with
+ * the CKU_SO user type.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ **/
+gboolean
+gck_session_init_pin (GckSession *self, const guchar *pin, gsize n_pin,
+                       GError **err)
+{
+	return gck_session_init_pin_full (self, pin, n_pin, NULL, err);
+}
+
+/**
+ * gck_session_init_pin_full:
+ * @self: Initialize PIN for this session's slot.
+ * @pin: The user's PIN, or NULL for protected authentication path.
+ * @n_pin: The length of the PIN.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error.
+ *
+ * Initialize the user's pin on this slot that this session is opened on.
+ * According to the PKCS#11 standards, the session must be logged in with
+ * the CKU_SO user type.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ **/
+gboolean
+gck_session_init_pin_full (GckSession *self, const guchar *pin, gsize n_pin,
+                            GCancellable *cancellable, GError **err)
+{
+	InitPin args = { GCK_ARGUMENTS_INIT, (guchar*)pin, n_pin };
+	return _gck_call_sync (self, perform_init_pin, NULL, &args, cancellable, err);
+
+}
+
+/**
+ * gck_session_init_pin_async:
+ * @self: Initialize PIN for this session's slot.
+ * @pin: The user's PIN, or NULL for protected authentication path.
+ * @n_pin: The length of the PIN.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Initialize the user's pin on this slot that this session is opened on.
+ * According to the PKCS#11 standards, the session must be logged in with
+ * the CKU_SO user type.
+ *
+ * This call will return immediately and completes asynchronously.
+ **/
+void
+gck_session_init_pin_async (GckSession *self, const guchar *pin, gsize n_pin,
+                             GCancellable *cancellable, GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+	InitPin* args = _gck_call_async_prep (self, self, perform_init_pin, NULL, sizeof (*args), free_init_pin);
+
+	args->pin = pin && n_pin ? g_memdup (pin, n_pin) : NULL;
+	args->n_pin = n_pin;
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_init_pin_finish:
+ * @self: The session.
+ * @result: The result passed to the callback.
+ * @err: A location to return an error.
+ *
+ * Get the result of initializing a user's PIN.
+ *
+ * Return value: Whether the operation was successful or not.
+ **/
+gboolean
+gck_session_init_pin_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	return _gck_call_basic_finish (result, err);
+}
+
+
+/* ---------------------------------------------------------------------------------------------
+ * SET PIN
+ */
+
+typedef struct _SetPin {
+	GckArguments base;
+	guchar *old_pin;
+	gsize n_old_pin;
+	guchar *new_pin;
+	gsize n_new_pin;
+} SetPin;
+
+static void
+free_set_pin (SetPin *args)
+{
+	g_free (args->old_pin);
+	g_free (args->new_pin);
+	g_free (args);
+}
+
+static CK_RV
+perform_set_pin (SetPin *args)
+{
+	return (args->base.pkcs11->C_SetPIN) (args->base.handle, (CK_BYTE_PTR)args->old_pin,
+	                                      args->n_old_pin, args->new_pin, args->n_new_pin);
+}
+
+/**
+ * gck_session_set_pin:
+ * @self: Change the PIN for this session's slot.
+ * @old_pin: The user's old PIN, or NULL for protected authentication path.
+ * @n_old_pin: The length of the PIN.
+ * @new_pin: The user's new PIN, or NULL for protected authentication path.
+ * @n_new_pin: The length of the PIN.
+ * @err: A location to return an error.
+ *
+ * Change the user's pin on this slot that this session is opened on.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ **/
+gboolean
+gck_session_set_pin (GckSession *self, const guchar *old_pin, gsize n_old_pin,
+                      const guchar *new_pin, gsize n_new_pin, GError **err)
+{
+	return gck_session_set_pin_full (self, old_pin, n_old_pin, new_pin, n_new_pin, NULL, err);
+}
+
+/**
+ * gck_session_set_pin_full:
+ * @self: Change the PIN for this session's slot.
+ * @old_pin: The user's old PIN, or NULL for protected authentication path.
+ * @n_old_pin: The length of the PIN.
+ * @new_pin: The user's new PIN, or NULL for protected authentication path.
+ * @n_new_pin: The length of the PIN.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error.
+ *
+ * Change the user's pin on this slot that this session is opened on.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ **/
+gboolean
+gck_session_set_pin_full (GckSession *self, const guchar *old_pin, gsize n_old_pin,
+                           const guchar *new_pin, gsize n_new_pin, GCancellable *cancellable,
+                           GError **err)
+{
+	SetPin args = { GCK_ARGUMENTS_INIT, (guchar*)old_pin, n_old_pin, (guchar*)new_pin, n_new_pin };
+	return _gck_call_sync (self, perform_set_pin, NULL, &args, cancellable, err);
+}
+
+/**
+ * gck_session_set_pin_async:
+ * @self: Change the PIN for this session's slot.
+ * @old_pin: The user's old PIN, or NULL for protected authentication path.
+ * @n_old_pin: The length of the PIN.
+ * @new_pin: The user's new PIN, or NULL for protected authentication path.
+ * @n_new_pin: The length of the PIN.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Change the user's pin on this slot that this session is opened on.
+ *
+ * This call will return immediately and completes asynchronously.
+ **/
+void
+gck_session_set_pin_async (GckSession *self, const guchar *old_pin, gsize n_old_pin,
+                            const guchar *new_pin, gsize n_new_pin, GCancellable *cancellable,
+                            GAsyncReadyCallback callback, gpointer user_data)
+{
+	SetPin* args = _gck_call_async_prep (self, self, perform_set_pin, NULL, sizeof (*args), free_set_pin);
+
+	args->old_pin = old_pin && n_old_pin ? g_memdup (old_pin, n_old_pin) : NULL;
+	args->n_old_pin = n_old_pin;
+	args->new_pin = new_pin && n_new_pin ? g_memdup (new_pin, n_new_pin) : NULL;
+	args->n_new_pin = n_new_pin;
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_set_pin_finish:
+ * @self: The session.
+ * @result: The result passed to the callback.
+ * @err: A location to return an error.
+ *
+ * Get the result of changing a user's PIN.
+ *
+ * Return value: Whether the operation was successful or not.
+ **/
+gboolean
+gck_session_set_pin_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	return _gck_call_basic_finish (result, err);
+}
+
+
+/* ---------------------------------------------------------------------------------------------
+ * LOGIN
+ */
+
+typedef struct _Login {
+	GckArguments base;
+	gulong user_type;
+	guchar *pin;
+	gsize n_pin;
+} Login;
+
+static void
+free_login (Login *args)
+{
+	g_free (args->pin);
+	g_free (args);
+}
+
+static CK_RV
+perform_login (Login *args)
+{
+	return (args->base.pkcs11->C_Login) (args->base.handle, args->user_type,
+	                                     (CK_BYTE_PTR)args->pin, args->n_pin);
+}
+
+/**
+ * gck_session_login:
+ * @self: Log in to this session.
+ * @user_type: The type of login user.
+ * @pin: The user's PIN, or NULL for protected authentication path.
+ * @n_pin: The length of the PIN.
+ * @err: A location to return an error.
+ *
+ * Login the user on the session. This call may block
+ * for an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ **/
+gboolean
+gck_session_login (GckSession *self, gulong user_type, const guchar *pin,
+                    gsize n_pin, GError **err)
+{
+	return gck_session_login_full (self, user_type, pin, n_pin, NULL, err);
+}
+
+/**
+ * gck_session_login_full:
+ * @self: Log in to this session.
+ * @user_type: The type of login user.
+ * @pin: The user's PIN, or NULL for protected authentication path.
+ * @n_pin: The length of the PIN.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error.
+ *
+ * Login the user on the session. This call may block for
+ * an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ **/
+gboolean
+gck_session_login_full (GckSession *self, gulong user_type, const guchar *pin,
+                         gsize n_pin, GCancellable *cancellable, GError **err)
+{
+	Login args = { GCK_ARGUMENTS_INIT, user_type, (guchar*)pin, n_pin };
+	return _gck_call_sync (self, perform_login, NULL, &args, cancellable, err);
+
+}
+
+/**
+ * gck_session_login_async:
+ * @self: Log in to this session.
+ * @user_type: The type of login user.
+ * @pin: The user's PIN, or NULL for protected authentication path.
+ * @n_pin: The length of the PIN.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Login the user on the session. This call will return
+ * immediately and completes asynchronously.
+ **/
+void
+gck_session_login_async (GckSession *self, gulong user_type, const guchar *pin,
+                          gsize n_pin, GCancellable *cancellable, GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+	Login* args = _gck_call_async_prep (self, self, perform_login, NULL, sizeof (*args), free_login);
+
+	args->user_type = user_type;
+	args->pin = pin && n_pin ? g_memdup (pin, n_pin) : NULL;
+	args->n_pin = n_pin;
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+
+}
+
+/**
+ * gck_session_login_finish:
+ * @self: The session logged into.
+ * @result: The result passed to the callback.
+ * @err: A location to return an error.
+ *
+ * Get the result of a login operation.
+ *
+ * Return value: Whether the operation was successful or not.
+ **/
+gboolean
+gck_session_login_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	return _gck_call_basic_finish (result, err);
+}
+
+
+
+
+/* LOGOUT */
+
+static CK_RV
+perform_logout (GckArguments *args)
+{
+	return (args->pkcs11->C_Logout) (args->handle);
+}
+
+/**
+ * gck_session_logout:
+ * @self: Logout of this session.
+ * @err: A location to return an error.
+ *
+ * Log out of the session. This call may block for an indefinite period.
+ *
+ * Return value: Whether the logout was successful or not.
+ **/
+gboolean
+gck_session_logout (GckSession *self, GError **err)
+{
+	return gck_session_logout_full (self, NULL, err);
+}
+
+/**
+ * gck_session_logout_full:
+ * @self: Logout of this session.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error.
+ *
+ * Log out of the session. This call may block for an indefinite period.
+ *
+ * Return value: Whether the logout was successful or not.
+ **/
+gboolean
+gck_session_logout_full (GckSession *self, GCancellable *cancellable, GError **err)
+{
+	GckArguments args = GCK_ARGUMENTS_INIT;
+	return _gck_call_sync (self, perform_logout, NULL, &args, cancellable, err);
+}
+
+/**
+ * gck_session_logout_async:
+ * @self: Logout of this session.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Log out of the session. This call returns immediately and completes
+ * asynchronously.
+ **/
+void
+gck_session_logout_async (GckSession *self, GCancellable *cancellable,
+                           GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckArguments *args = _gck_call_async_prep (self, self, perform_logout, NULL, 0, NULL);
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_logout_finish:
+ * @self: Logout of this session.
+ * @result: The result passed to the callback.
+ * @err: A location to return an error.
+ *
+ * Get the result of logging out of a session.
+ *
+ * Return value: Whether the logout was successful or not.
+ **/
+gboolean
+gck_session_logout_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	return _gck_call_basic_finish (result, err);
+}
+
+
+
+
+/* CREATE OBJECT */
+
+typedef struct _CreateObject {
+	GckArguments base;
+	GckAttributes *attrs;
+	CK_OBJECT_HANDLE object;
+} CreateObject;
+
+static void
+free_create_object (CreateObject *args)
+{
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+static CK_RV
+perform_create_object (CreateObject *args)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	CK_ULONG n_attrs;
+
+	attrs = _gck_attributes_commit_out (args->attrs, &n_attrs);
+
+	return (args->base.pkcs11->C_CreateObject) (args->base.handle,
+	                                            attrs, n_attrs,
+	                                            &args->object);
+}
+
+/**
+ * gck_session_create_object:
+ * @self: The session to create the object on.
+ * @err: A location to store an error.
+ * @...: The attributes to create the new object with.
+ *
+ * Create a new PKCS#11 object. This call may block
+ * for an indefinite period.
+ *
+ * The arguments must be triples of: attribute type, data type, value
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG,
+ * 				GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ *
+ * The variable argument list should be terminated with GCK_INVALID.</para>
+ *
+ * Return value: The newly created object, or NULL if an error occurred.
+ **/
+GckObject*
+gck_session_create_object (GckSession *self, GError **err, ...)
+{
+	GckAttributes *attrs;
+	GckObject *object;
+	va_list va;
+
+	va_start (va, err);
+	attrs = gck_attributes_new_valist (g_realloc, va);
+	va_end (va);
+
+	object = gck_session_create_object_full (self, attrs, NULL, err);
+	gck_attributes_unref (attrs);
+	return object;
+}
+
+/**
+ * gck_session_create_object_full:
+ * @self: The session to create the object on.
+ * @attrs: The attributes to create the object with.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error, or NULL.
+ *
+ * Create a new PKCS#11 object. This call may block for an
+ * indefinite period.
+ *
+ * Return value: The newly created object or NULL if an error occurred.
+ **/
+GckObject*
+gck_session_create_object_full (GckSession *self, GckAttributes *attrs,
+                                 GCancellable *cancellable, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	CreateObject args = { GCK_ARGUMENTS_INIT, attrs, 0 };
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+	g_return_val_if_fail (attrs, NULL);
+
+	_gck_attributes_lock (attrs);
+	ret = _gck_call_sync (self, perform_create_object, NULL, &args, cancellable, err);
+	_gck_attributes_unlock (attrs);
+
+	if (!ret)
+		return NULL;
+
+	return gck_object_from_handle (data->slot, args.object);
+}
+
+/**
+ * gck_session_create_object_async:
+ * @self: The session to create the object on.
+ * @attrs: The attributes to create the object with.
+ * @cancellable: Optional cancellation object or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Create a new PKCS#11 object. This call will return immediately
+ * and complete asynchronously.
+ **/
+void
+gck_session_create_object_async (GckSession *self, GckAttributes *attrs,
+                                  GCancellable *cancellable, GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	CreateObject *args = _gck_call_async_prep (self, self, perform_create_object,
+	                                            NULL, sizeof (*args), free_create_object);
+
+	g_return_if_fail (attrs);
+
+	args->attrs = gck_attributes_ref (attrs);
+	_gck_attributes_lock (attrs);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_create_object_finish:
+ * @self: The session to create the object on.
+ * @result: The result passed to the callback.
+ * @err: A location to return an error, or NULL.
+ *
+ * Get the result of creating a new PKCS#11 object.
+ *
+ * Return value: The newly created object or NULL if an error occurred.
+ **/
+GckObject*
+gck_session_create_object_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	CreateObject *args;
+
+	args = _gck_call_arguments (result, CreateObject);
+	_gck_attributes_unlock (args->attrs);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+	return gck_object_from_handle (data->slot, args->object);
+}
+
+
+
+/* FIND OBJECTS */
+
+typedef struct _FindObjects {
+	GckArguments base;
+	GckAttributes *attrs;
+	CK_OBJECT_HANDLE_PTR objects;
+	CK_ULONG n_objects;
+} FindObjects;
+
+static void
+free_find_objects (FindObjects *args)
+{
+	gck_attributes_unref (args->attrs);
+	g_free (args->objects);
+}
+
+static CK_RV
+perform_find_objects (FindObjects *args)
+{
+	CK_OBJECT_HANDLE_PTR batch;
+	CK_ULONG n_batch, n_found;
+	CK_ATTRIBUTE_PTR attrs;
+	CK_ULONG n_attrs;
+	GArray *array;
+	CK_RV rv;
+
+	attrs = _gck_attributes_commit_out (args->attrs, &n_attrs);
+
+	rv = (args->base.pkcs11->C_FindObjectsInit) (args->base.handle,
+	                                             attrs, n_attrs);
+	if (rv != CKR_OK)
+		return rv;
+
+	batch = NULL;
+	n_found = n_batch = 4;
+	array = g_array_new (0, 1, sizeof (CK_OBJECT_HANDLE));
+
+	do {
+		/*
+		 * Reallocate and double in size:
+		 *  - First time.
+		 *  - Each time we found as many as batch
+		 */
+
+		if (n_found == n_batch) {
+			n_batch *= 2;
+			batch = g_realloc (batch, sizeof (CK_OBJECT_HANDLE) * n_batch);
+		}
+
+		rv = (args->base.pkcs11->C_FindObjects) (args->base.handle,
+		                                         batch, n_batch, &n_found);
+		if (rv != CKR_OK)
+			break;
+
+		g_array_append_vals (array, batch, n_found);
+
+	} while (n_found > 0);
+
+	g_free (batch);
+
+	if (rv == CKR_OK) {
+		args->n_objects = array->len;
+		args->objects = (CK_OBJECT_HANDLE_PTR)g_array_free (array, FALSE);
+		rv = (args->base.pkcs11->C_FindObjectsFinal) (args->base.handle);
+	} else {
+		args->objects = NULL;
+		args->n_objects = 0;
+		g_array_free (array, TRUE);
+	}
+
+	return rv;
+}
+
+static GList*
+objlist_from_handles (GckSession *self, CK_OBJECT_HANDLE_PTR objects,
+                      CK_ULONG n_objects)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	GList *results = NULL;
+
+	while (n_objects > 0) {
+		results = g_list_prepend (results,
+		                gck_object_from_handle (data->slot, objects[--n_objects]));
+	}
+
+	return g_list_reverse (results);
+}
+
+/**
+ * gck_session_find_objects:
+ * @self: The session to find objects on.
+ * @err: A location to return an error or NULL.
+ * @...: The attributes to match.
+ *
+ * Find objects matching the passed attributes. This call may
+ * block for an indefinite period.
+ *
+ * The arguments must be triples of: attribute type, data type, value
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG,
+ * 				GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with GCK_INVALID.</para>
+ *
+ * Return value: A list of the matching objects, which may be empty.
+ **/
+GList*
+gck_session_find_objects (GckSession *self, GError **err, ...)
+{
+	GckAttributes *attrs;
+	GList *results;
+	va_list va;
+
+	va_start (va, err);
+	attrs = gck_attributes_new_valist (g_realloc, va);
+	va_end (va);
+
+	results = gck_session_find_objects_full (self, attrs, NULL, err);
+	gck_attributes_unref (attrs);
+	return results;
+}
+
+/**
+ * gck_session_find_objects_full:
+ * @self: The session to find objects on.
+ * @attrs: The attributes to match.
+ * @cancellable: Optional cancellation object or NULL.
+ * @err: A location to return an error or NULL.
+ *
+ * Find the objects matching the passed attributes. This call may
+ * block for an indefinite period.
+ *
+ * Return value: A list of the matching objects, which may be empty.
+ **/
+GList*
+gck_session_find_objects_full (GckSession *self, GckAttributes *attrs,
+                                GCancellable *cancellable, GError **err)
+{
+	FindObjects args = { GCK_ARGUMENTS_INIT, attrs, NULL, 0 };
+	GList *results = NULL;
+
+	g_return_val_if_fail (attrs, NULL);
+	_gck_attributes_lock (attrs);
+
+	if (_gck_call_sync (self, perform_find_objects, NULL, &args, cancellable, err))
+		results = objlist_from_handles (self, args.objects, args.n_objects);
+
+	g_free (args.objects);
+	_gck_attributes_unlock (attrs);
+	return results;
+}
+
+/**
+ * gck_session_find_objects_async:
+ * @self: The session to find objects on.
+ * @attrs: The attributes to match.
+ * @cancellable: Optional cancellation object or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Find the objects matching the passed attributes. This call will
+ * return immediately and complete asynchronously.
+ **/
+void
+gck_session_find_objects_async (GckSession *self, GckAttributes *attrs,
+                                 GCancellable *cancellable, GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	FindObjects *args = _gck_call_async_prep (self, self, perform_find_objects,
+	                                           NULL, sizeof (*args), free_find_objects);
+	args->attrs = gck_attributes_ref (attrs);
+	_gck_attributes_lock (attrs);
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_find_objects_finish:
+ * @self: The session to find objects on.
+ * @result: The attributes to match.
+ * @err: A location to return an error.
+ *
+ * Get the result of a find operation.
+ *
+ * Return value: A list of the matching objects, which may be empty.
+ **/
+GList*
+gck_session_find_objects_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	FindObjects *args;
+
+	args = _gck_call_arguments (result, FindObjects);
+	_gck_attributes_unlock (args->attrs);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+	return objlist_from_handles (self, args->objects, args->n_objects);
+}
+
+/* -----------------------------------------------------------------------------
+ * KEY PAIR GENERATION
+ */
+
+typedef struct _GenerateKeyPair {
+	GckArguments base;
+	GckMechanism *mechanism;
+	GckAttributes *public_attrs;
+	GckAttributes *private_attrs;
+	CK_OBJECT_HANDLE public_key;
+	CK_OBJECT_HANDLE private_key;
+} GenerateKeyPair;
+
+static void
+free_generate_key_pair (GenerateKeyPair *args)
+{
+	gck_mechanism_unref (args->mechanism);
+	gck_attributes_unref (args->public_attrs);
+	gck_attributes_unref (args->private_attrs);
+	g_free (args);
+}
+
+static CK_RV
+perform_generate_key_pair (GenerateKeyPair *args)
+{
+	CK_ATTRIBUTE_PTR pub_attrs, priv_attrs;
+	CK_ULONG n_pub_attrs, n_priv_attrs;
+
+	g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism));
+
+	pub_attrs = _gck_attributes_commit_out (args->public_attrs, &n_pub_attrs);
+	priv_attrs = _gck_attributes_commit_out (args->private_attrs, &n_priv_attrs);
+
+	return (args->base.pkcs11->C_GenerateKeyPair) (args->base.handle,
+	                                               (CK_MECHANISM_PTR)args->mechanism,
+	                                               pub_attrs, n_pub_attrs,
+	                                               priv_attrs, n_priv_attrs,
+	                                               &args->public_key,
+	                                               &args->private_key);
+}
+
+/**
+ * gck_session_generate_key_pair_full:
+ * @self: The session to use.
+ * @mechanism: The mechanism to use for key generation.
+ * @public_attrs: Additional attributes for the generated public key.
+ * @private_attrs: Additional attributes for the generated private key.
+ * @public_key: A location to return the resulting public key.
+ * @private_key: A location to return the resulting private key.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error, or NULL.
+ *
+ * Generate a new key pair of public and private keys. This call may block for an
+ * indefinite period.
+ *
+ * Return value: TRUE if the operation succeeded.
+ **/
+gboolean
+gck_session_generate_key_pair_full (GckSession *self, GckMechanism *mechanism,
+                                     GckAttributes *public_attrs, GckAttributes *private_attrs,
+                                     GckObject **public_key, GckObject **private_key,
+                                     GCancellable *cancellable, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	GenerateKeyPair args = { GCK_ARGUMENTS_INIT, mechanism, public_attrs, private_attrs, 0, 0 };
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (mechanism, FALSE);
+	g_return_val_if_fail (public_attrs, FALSE);
+	g_return_val_if_fail (private_attrs, FALSE);
+	g_return_val_if_fail (public_key, FALSE);
+	g_return_val_if_fail (private_key, FALSE);
+
+	_gck_attributes_lock (public_attrs);
+	if (public_attrs != private_attrs)
+		_gck_attributes_lock (private_attrs);
+	ret = _gck_call_sync (self, perform_generate_key_pair, NULL, &args, cancellable, err);
+	if (public_attrs != private_attrs)
+		_gck_attributes_unlock (private_attrs);
+	_gck_attributes_unlock (public_attrs);
+
+	if (!ret)
+		return FALSE;
+
+	*public_key = gck_object_from_handle (data->slot, args.public_key);
+	*private_key = gck_object_from_handle (data->slot, args.private_key);
+	return TRUE;
+}
+
+/**
+ * gck_session_generate_key_pair_async:
+ * @self: The session to use.
+ * @mechanism: The mechanism to use for key generation.
+ * @public_attrs: Additional attributes for the generated public key.
+ * @private_attrs: Additional attributes for the generated private key.
+ * @cancellable: Optional cancellation object or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Generate a new key pair of public and private keys. This call will
+ * return immediately and complete asynchronously.
+ **/
+void
+gck_session_generate_key_pair_async (GckSession *self, GckMechanism *mechanism,
+                                      GckAttributes *public_attrs, GckAttributes *private_attrs,
+                                      GCancellable *cancellable, GAsyncReadyCallback callback,
+                                      gpointer user_data)
+{
+	GenerateKeyPair *args = _gck_call_async_prep (self, self, perform_generate_key_pair,
+	                                               NULL, sizeof (*args), free_generate_key_pair);
+
+	g_return_if_fail (GCK_IS_SESSION (self));
+	g_return_if_fail (mechanism);
+	g_return_if_fail (public_attrs);
+	g_return_if_fail (private_attrs);
+
+	args->public_attrs = gck_attributes_ref (public_attrs);
+	_gck_attributes_lock (public_attrs);
+	args->private_attrs = gck_attributes_ref (private_attrs);
+	if (public_attrs != private_attrs)
+		_gck_attributes_lock (private_attrs);
+	args->mechanism = gck_mechanism_ref (mechanism);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_generate_key_pair_finish:
+ * @self: The session to use.
+ * @result: The async result passed to the callback.
+ * @public_key: A location to return the resulting public key.
+ * @private_key: A location to return the resulting private key.
+ * @err: A location to return an error.
+ *
+ * Get the result of a generate key pair operation.
+ *
+ * Return value: TRUE if the operation succeeded.
+ **/
+gboolean
+gck_session_generate_key_pair_finish (GckSession *self, GAsyncResult *result,
+                                       GckObject **public_key, GckObject **private_key,
+                                       GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	GenerateKeyPair *args;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (public_key, FALSE);
+	g_return_val_if_fail (private_key, FALSE);
+
+	args = _gck_call_arguments (result, GenerateKeyPair);
+	_gck_attributes_unlock (args->public_attrs);
+	if (args->public_attrs != args->private_attrs)
+		_gck_attributes_unlock (args->private_attrs);
+
+	if (!_gck_call_basic_finish (result, err))
+		return FALSE;
+
+	*public_key = gck_object_from_handle (data->slot, args->public_key);
+	*private_key = gck_object_from_handle (data->slot, args->private_key);
+	return TRUE;
+}
+
+/* -----------------------------------------------------------------------------
+ * KEY WRAPPING
+ */
+
+typedef struct _WrapKey {
+	GckArguments base;
+	GckMechanism *mechanism;
+	CK_OBJECT_HANDLE wrapper;
+	CK_OBJECT_HANDLE wrapped;
+	gpointer result;
+	gulong n_result;
+} WrapKey;
+
+static void
+free_wrap_key (WrapKey *args)
+{
+	gck_mechanism_unref (args->mechanism);
+	g_free (args->result);
+	g_free (args);
+}
+
+static CK_RV
+perform_wrap_key (WrapKey *args)
+{
+	CK_RV rv;
+
+	g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism));
+
+	/* Get the length of the result */
+	rv = (args->base.pkcs11->C_WrapKey) (args->base.handle,
+	                                     (CK_MECHANISM_PTR)args->mechanism,
+	                                     args->wrapper, args->wrapped,
+	                                     NULL, &args->n_result);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* And try again with a real buffer */
+	args->result = g_malloc0 (args->n_result);
+	return (args->base.pkcs11->C_WrapKey) (args->base.handle,
+	                                       (CK_MECHANISM_PTR)args->mechanism,
+	                                       args->wrapper, args->wrapped,
+	                                       args->result, &args->n_result);
+}
+
+/**
+ * gck_session_wrap_key:
+ * @self: The session to use.
+ * @wrapper: The key to use for wrapping.
+ * @mechanism: The mechanism type to use for wrapping.
+ * @wrapped: The key to wrap.
+ * @n_result: A location in which to return the length of the wrapped data.
+ * @err: A location to return an error, or NULL.
+ *
+ * Wrap a key into a byte stream. This call may block for an
+ * indefinite period.
+ *
+ * Return value: The wrapped data or NULL if the operation failed.
+ **/
+gpointer
+gck_session_wrap_key (GckSession *self, GckObject *key, gulong mech_type,
+                       GckObject *wrapped, gsize *n_result, GError **err)
+{
+	GckMechanism mech = { mech_type, NULL, 0 };
+	return gck_session_wrap_key_full (self, key, &mech, wrapped, n_result, NULL, err);
+}
+
+/**
+ * gck_session_wrap_key_full:
+ * @self: The session to use.
+ * @wrapper: The key to use for wrapping.
+ * @mechanism: The mechanism to use for wrapping.
+ * @wrapped: The key to wrap.
+ * @n_result: A location in which to return the length of the wrapped data.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error, or NULL.
+ *
+ * Wrap a key into a byte stream. This call may block for an
+ * indefinite period.
+ *
+ * Return value: The wrapped data or NULL if the operation failed.
+ **/
+gpointer
+gck_session_wrap_key_full (GckSession *self, GckObject *wrapper, GckMechanism *mechanism,
+                            GckObject *wrapped, gsize *n_result, GCancellable *cancellable,
+                            GError **err)
+{
+	WrapKey args = { GCK_ARGUMENTS_INIT, mechanism, 0, 0, NULL, 0 };
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (mechanism, FALSE);
+	g_return_val_if_fail (GCK_IS_OBJECT (wrapped), FALSE);
+	g_return_val_if_fail (GCK_IS_OBJECT (wrapper), FALSE);
+	g_return_val_if_fail (n_result, FALSE);
+
+	g_object_get (wrapper, "handle", &args.wrapper, NULL);
+	g_return_val_if_fail (args.wrapper != 0, NULL);
+	g_object_get (wrapped, "handle", &args.wrapped, NULL);
+	g_return_val_if_fail (args.wrapped != 0, NULL);
+
+	ret = _gck_call_sync (self, perform_wrap_key, NULL, &args, cancellable, err);
+
+	if (!ret)
+		return FALSE;
+
+	*n_result = args.n_result;
+	return args.result;
+}
+
+/**
+ * gck_session_wrap_key_async:
+ * @self: The session to use.
+ * @wrapper: The key to use for wrapping.
+ * @mechanism: The mechanism to use for wrapping.
+ * @wrapped: The key to wrap.
+ * @cancellable: Optional cancellation object or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Wrap a key into a byte stream. This call will
+ * return immediately and complete asynchronously.
+ **/
+void
+gck_session_wrap_key_async (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                             GckObject *wrapped, GCancellable *cancellable,
+                             GAsyncReadyCallback callback, gpointer user_data)
+{
+	WrapKey *args = _gck_call_async_prep (self, self, perform_wrap_key,
+	                                       NULL, sizeof (*args), free_wrap_key);
+
+	g_return_if_fail (GCK_IS_SESSION (self));
+	g_return_if_fail (mechanism);
+	g_return_if_fail (GCK_IS_OBJECT (wrapped));
+	g_return_if_fail (GCK_IS_OBJECT (key));
+
+	args->mechanism = gck_mechanism_ref (mechanism);
+	g_object_get (key, "handle", &args->wrapper, NULL);
+	g_return_if_fail (args->wrapper != 0);
+	g_object_get (wrapped, "handle", &args->wrapped, NULL);
+	g_return_if_fail (args->wrapped != 0);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_wrap_key_finish:
+ * @self: The session to use.
+ * @result: The async result passed to the callback.
+ * @n_result: A location in which to return the length of the wrapped data.
+ * @err: A location to return an error.
+ *
+ * Get the result of a wrap key operation.
+ *
+ * Return value: The wrapped data or NULL if the operation failed.
+ **/
+gpointer
+gck_session_wrap_key_finish (GckSession *self, GAsyncResult *result,
+                              gsize *n_result, GError **err)
+{
+	WrapKey *args;
+	gpointer ret;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+	g_return_val_if_fail (n_result, NULL);
+
+	args = _gck_call_arguments (result, WrapKey);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+
+	*n_result = args->n_result;
+	args->n_result = 0;
+	ret = args->result;
+	args->result = NULL;
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * KEY UNWRAPPING
+ */
+
+typedef struct _UnwrapKey {
+	GckArguments base;
+	GckMechanism *mechanism;
+	GckAttributes *attrs;
+	CK_OBJECT_HANDLE wrapper;
+	gconstpointer input;
+	gulong n_input;
+	CK_OBJECT_HANDLE unwrapped;
+} UnwrapKey;
+
+static void
+free_unwrap_key (UnwrapKey *args)
+{
+	gck_mechanism_unref (args->mechanism);
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+static CK_RV
+perform_unwrap_key (UnwrapKey *args)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	CK_ULONG n_attrs;
+
+	g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism));
+
+	attrs = _gck_attributes_commit_out (args->attrs, &n_attrs);
+
+	return (args->base.pkcs11->C_UnwrapKey) (args->base.handle,
+	                                         (CK_MECHANISM_PTR)args->mechanism,
+	                                         args->wrapper, (CK_BYTE_PTR)args->input,
+	                                         args->n_input, attrs, n_attrs,
+	                                         &args->unwrapped);
+}
+
+/**
+ * gck_session_unwrap_key:
+ * @self: The session to use.
+ * @wrapper: The key to use for unwrapping.
+ * @mech_type: The mechanism type to use for derivation.
+ * @input: The wrapped data as a byte stream.
+ * @n_input: The length of the wrapped data.
+ * @err: A location to return an error, or NULL.
+ * @...: Additional attributes for the unwrapped key.
+ *
+ * Unwrap a key from a byte stream. This call may block for an
+ * indefinite period.
+ *
+ * The arguments must be triples of: attribute type, data type, value
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG,
+ * 				GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with GCK_INVALID.</para>
+ *
+ * Return value: The new unwrapped key or NULL if the operation failed.
+ **/
+GckObject*
+gck_session_unwrap_key (GckSession *self, GckObject *key, gulong mech_type,
+                         gconstpointer input, gsize n_input, GError **err, ...)
+{
+	GckMechanism mech = { mech_type, NULL, 0 };
+	GckAttributes *attrs;
+	GckObject *object;
+	va_list va;
+
+	va_start (va, err);
+	attrs = gck_attributes_new_valist (g_realloc, va);
+	va_end (va);
+
+	object = gck_session_unwrap_key_full (self, key, &mech, input, n_input, attrs, NULL, err);
+	gck_attributes_unref (attrs);
+	return object;
+}
+
+/**
+ * gck_session_unwrap_key_full:
+ * @self: The session to use.
+ * @wrapper: The key to use for unwrapping.
+ * @mechanism: The mechanism to use for unwrapping.
+ * @input: The wrapped data as a byte stream.
+ * @n_input: The length of the wrapped data.
+ * @attrs: Additional attributes for the unwrapped key.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error, or NULL.
+ *
+ * Unwrap a key from a byte stream. This call may block for an
+ * indefinite period.
+ *
+ * Return value: The new unwrapped key or NULL if the operation failed.
+ **/
+GckObject*
+gck_session_unwrap_key_full (GckSession *self, GckObject *wrapper, GckMechanism *mechanism,
+                              gconstpointer input, gsize n_input, GckAttributes *attrs,
+                              GCancellable *cancellable, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	UnwrapKey args = { GCK_ARGUMENTS_INIT, mechanism, attrs, 0, input, n_input, 0 };
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (GCK_IS_OBJECT (wrapper), FALSE);
+	g_return_val_if_fail (mechanism, FALSE);
+	g_return_val_if_fail (attrs, FALSE);
+
+	g_object_get (wrapper, "handle", &args.wrapper, NULL);
+	g_return_val_if_fail (args.wrapper != 0, NULL);
+
+	_gck_attributes_lock (attrs);
+	ret = _gck_call_sync (self, perform_unwrap_key, NULL, &args, cancellable, err);
+	_gck_attributes_unlock (attrs);
+
+	if (!ret)
+		return NULL;
+
+	return gck_object_from_handle (data->slot, args.unwrapped);
+}
+
+/**
+ * gck_session_unwrap_key_async:
+ * @self: The session to use.
+ * @wrapper: The key to use for unwrapping.
+ * @mechanism: The mechanism to use for unwrapping.
+ * @input: The wrapped data as a byte stream.
+ * @n_input: The length of the wrapped data.
+ * @attrs: Additional attributes for the unwrapped key.
+ * @cancellable: Optional cancellation object or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Unwrap a key from a byte stream. This call will
+ * return immediately and complete asynchronously.
+ **/
+void
+gck_session_unwrap_key_async (GckSession *self, GckObject *wrapper, GckMechanism *mechanism,
+                               gconstpointer input, gsize n_input, GckAttributes *attrs,
+                               GCancellable *cancellable, GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	UnwrapKey *args = _gck_call_async_prep (self, self, perform_unwrap_key,
+	                                         NULL, sizeof (*args), free_unwrap_key);
+
+	g_return_if_fail (GCK_IS_SESSION (self));
+	g_return_if_fail (GCK_IS_OBJECT (wrapper));
+	g_return_if_fail (attrs);
+
+	g_object_get (wrapper, "handle", &args->wrapper, NULL);
+	g_return_if_fail (args->wrapper != 0);
+
+	args->mechanism = gck_mechanism_ref (mechanism);
+	args->attrs = gck_attributes_ref (attrs);
+	args->input = input;
+	args->n_input = n_input;
+	_gck_attributes_lock (attrs);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_wrap_key_finish:
+ * @self: The session to use.
+ * @result: The async result passed to the callback.
+ * @err: A location to return an error.
+ *
+ * Get the result of a unwrap key operation.
+ *
+ * Return value: The new unwrapped key or NULL if the operation failed.
+ **/
+GckObject*
+gck_session_unwrap_key_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	UnwrapKey *args;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+
+	args = _gck_call_arguments (result, UnwrapKey);
+	_gck_attributes_unlock (args->attrs);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+	return gck_object_from_handle (data->slot, args->unwrapped);
+}
+
+/* -----------------------------------------------------------------------------
+ * KEY DERIVATION
+ */
+
+typedef struct _DeriveKey {
+	GckArguments base;
+	GckMechanism *mechanism;
+	GckAttributes *attrs;
+	CK_OBJECT_HANDLE key;
+	CK_OBJECT_HANDLE derived;
+} DeriveKey;
+
+static void
+free_derive_key (DeriveKey *args)
+{
+	gck_mechanism_unref (args->mechanism);
+	gck_attributes_unref (args->attrs);
+	g_free (args);
+}
+
+static CK_RV
+perform_derive_key (DeriveKey *args)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	CK_ULONG n_attrs;
+
+	g_assert (sizeof (CK_MECHANISM) == sizeof (GckMechanism));
+
+	attrs = _gck_attributes_commit_out (args->attrs, &n_attrs);
+
+	return (args->base.pkcs11->C_DeriveKey) (args->base.handle,
+	                                         (CK_MECHANISM_PTR)args->mechanism,
+	                                         args->key, attrs, n_attrs,
+	                                         &args->derived);
+}
+
+/**
+ * gck_session_derive_key_full:
+ * @self: The session to use.
+ * @base: The key to derive from.
+ * @mech_type: The mechanism type to use for derivation.
+ * @err: A location to return an error, or NULL.
+ * @...: Additional attributes for the derived key.
+ *
+ * Derive a key from another key. This call may block for an
+ * indefinite period.
+ *
+ * The arguments must be triples of: attribute type, data type, value
+ *
+ * <para>The variable argument list should contain:
+ * 	<variablelist>
+ *		<varlistentry>
+ * 			<term>a)</term>
+ * 			<listitem><para>The gulong attribute type (ie: CKA_LABEL). </para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>b)</term>
+ * 			<listitem><para>The attribute data type (one of GCK_BOOLEAN, GCK_ULONG,
+ * 				GCK_STRING, GCK_DATE) orthe raw attribute value length.</para></listitem>
+ * 		</varlistentry>
+ * 		<varlistentry>
+ * 			<term>c)</term>
+ * 			<listitem><para>The attribute value, either a gboolean, gulong, gchar*, GDate* or
+ * 				a pointer to a raw attribute value.</para></listitem>
+ * 		</varlistentry>
+ * 	</variablelist>
+ * The variable argument list should be terminated with GCK_INVALID.</para>
+ *
+ * Return value: The new derived key or NULL if the operation failed.
+ **/
+GckObject*
+gck_session_derive_key (GckSession *self, GckObject *key, gulong mech_type,
+                         GError **err, ...)
+{
+	GckMechanism mech = { mech_type, NULL, 0 };
+	GckAttributes *attrs;
+	GckObject *object;
+	va_list va;
+
+	va_start (va, err);
+	attrs = gck_attributes_new_valist (g_realloc, va);
+	va_end (va);
+
+	object = gck_session_derive_key_full (self, key, &mech, attrs, NULL, err);
+	gck_attributes_unref (attrs);
+	return object;
+}
+
+/**
+ * gck_session_derive_key_full:
+ * @self: The session to use.
+ * @base: The key to derive from.
+ * @mechanism: The mechanism to use for derivation.
+ * @attrs: Additional attributes for the derived key.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error, or NULL.
+ *
+ * Derive a key from another key. This call may block for an
+ * indefinite period.
+ *
+ * Return value: The new derived key or NULL if the operation failed.
+ **/
+GckObject*
+gck_session_derive_key_full (GckSession *self, GckObject *base, GckMechanism *mechanism,
+                              GckAttributes *attrs, GCancellable *cancellable, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	DeriveKey args = { GCK_ARGUMENTS_INIT, mechanism, attrs, 0, 0 };
+	gboolean ret;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), FALSE);
+	g_return_val_if_fail (GCK_IS_OBJECT (base), FALSE);
+	g_return_val_if_fail (mechanism, FALSE);
+	g_return_val_if_fail (attrs, FALSE);
+
+	g_object_get (base, "handle", &args.key, NULL);
+	g_return_val_if_fail (args.key != 0, NULL);
+
+	_gck_attributes_lock (attrs);
+	ret = _gck_call_sync (self, perform_derive_key, NULL, &args, cancellable, err);
+	_gck_attributes_unlock (attrs);
+
+	if (!ret)
+		return NULL;
+
+	return gck_object_from_handle (data->slot, args.derived);
+}
+
+/**
+ * gck_session_derive_key_async:
+ * @self: The session to use.
+ * @base: The key to derive from.
+ * @mechanism: The mechanism to use for derivation.
+ * @attrs: Additional attributes for the derived key.
+ * @cancellable: Optional cancellation object or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Derive a key from another key. This call will
+ * return immediately and complete asynchronously.
+ **/
+void
+gck_session_derive_key_async (GckSession *self, GckObject *base, GckMechanism *mechanism,
+                               GckAttributes *attrs, GCancellable *cancellable,
+                               GAsyncReadyCallback callback, gpointer user_data)
+{
+	DeriveKey *args = _gck_call_async_prep (self, self, perform_derive_key,
+	                                         NULL, sizeof (*args), free_derive_key);
+
+	g_return_if_fail (GCK_IS_SESSION (self));
+	g_return_if_fail (GCK_IS_OBJECT (base));
+	g_return_if_fail (attrs);
+
+	g_object_get (base, "handle", &args->key, NULL);
+	g_return_if_fail (args->key != 0);
+
+	args->mechanism = gck_mechanism_ref (mechanism);
+	args->attrs = gck_attributes_ref (attrs);
+	_gck_attributes_lock (attrs);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_wrap_key_finish:
+ * @self: The session to use.
+ * @result: The async result passed to the callback.
+ * @err: A location to return an error.
+ *
+ * Get the result of a derive key operation.
+ *
+ * Return value: The new derived key or NULL if the operation failed.
+ **/
+GckObject*
+gck_session_derive_key_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	GckSessionData *data = GCK_SESSION_GET_DATA (self);
+	DeriveKey *args;
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
+
+	args = _gck_call_arguments (result, DeriveKey);
+	_gck_attributes_unlock (args->attrs);
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+
+	return gck_object_from_handle (data->slot, args->derived);
+}
+
+/* --------------------------------------------------------------------------------------------------
+ * AUTHENTICATE
+ */
+
+typedef enum _AuthenticateState {
+	AUTHENTICATE_NONE,
+	AUTHENTICATE_CAN,
+	AUTHENTICATE_WANT,
+	AUTHENTICATE_PERFORM
+} AuthenticateState;
+
+typedef struct _Authenticate {
+	AuthenticateState state;
+	gboolean protected_auth;
+	GckModule *module;
+	GckObject *object;
+	gchar *label;
+	gchar *password;
+} Authenticate;
+
+static CK_RV
+authenticate_perform (Authenticate *args, GckArguments *base)
+{
+	CK_ATTRIBUTE attributes[2];
+	CK_OBJECT_HANDLE handle;
+	CK_ULONG pin_len;
+	CK_BBOOL bvalue;
+	CK_RV rv;
+
+	g_assert (args);
+	g_assert (base);
+
+	switch (args->state) {
+
+	/*
+	 * Cannot authenticate for whatever reason, perhaps not
+	 * enabled, or failed incomprehensibly etc.
+	 *
+	 */
+	case AUTHENTICATE_NONE:
+		return CKR_OK;
+
+	/*
+	 * Can authenticate but haven't seen if we should, yet
+	 * check out the object in question.
+	 */
+	case AUTHENTICATE_CAN:
+
+		handle = gck_object_get_handle (args->object);
+
+		attributes[0].type = CKA_LABEL;
+		attributes[0].pValue = NULL;
+		attributes[0].ulValueLen = 0;
+		attributes[1].type = CKA_ALWAYS_AUTHENTICATE;
+		attributes[1].pValue = &bvalue;
+		attributes[1].ulValueLen = sizeof (bvalue);
+
+		rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2);
+		if (rv == CKR_ATTRIBUTE_TYPE_INVALID)
+			bvalue = CK_FALSE;
+		else if (rv != CKR_OK)
+			return rv;
+
+		/* No authentication needed, on this object */
+		if (bvalue != CK_TRUE) {
+			args->state = AUTHENTICATE_NONE;
+			return CKR_OK;
+		}
+
+		/* Protected authentication path, just go to perform */
+		if (args->protected_auth) {
+			args->state = AUTHENTICATE_PERFORM;
+			return authenticate_perform (args, base);
+		}
+
+		/* Get the label for a prompt */
+		g_assert (!args->label);
+		if (attributes[0].ulValueLen) {
+			attributes[0].pValue = g_malloc0 (attributes[0].ulValueLen + 1);
+			rv = (base->pkcs11->C_GetAttributeValue) (base->handle, handle, attributes, 2);
+			if (rv == CKR_OK) {
+				g_assert (!args->label);
+				args->label = attributes[0].pValue;
+				args->label[attributes[0].ulValueLen] = 0;
+			} else {
+				g_free (attributes[0].pValue);
+			}
+		}
+
+		/* Need a password */
+		args->state = AUTHENTICATE_WANT;
+		return CKR_USER_NOT_LOGGED_IN;
+
+	/*
+	 * This state should be handled in verify_authenticate.
+	 */
+	case AUTHENTICATE_WANT:
+		g_assert (FALSE);
+		return CKR_GENERAL_ERROR;
+
+	/*
+	 * Do the actual login authentication.
+	 */
+	case AUTHENTICATE_PERFORM:
+		pin_len = args->password ? strlen (args->password) : 0;
+		rv = (base->pkcs11->C_Login) (base->handle, CKU_CONTEXT_SPECIFIC,
+		                              (CK_UTF8CHAR_PTR)args->password, pin_len);
+		if (rv == CKR_PIN_INCORRECT && !args->protected_auth)
+			args->state = AUTHENTICATE_WANT;
+		else
+			args->state = AUTHENTICATE_NONE;
+		return rv;
+
+	default:
+		g_assert_not_reached ();
+		return CKR_GENERAL_ERROR;
+	}
+}
+
+static gboolean
+authenticate_complete (Authenticate *auth, GckArguments *base, CK_RV result)
+{
+	g_assert (auth);
+	g_assert (base);
+
+	/* We're done here if not in this state */
+	if (auth->state == AUTHENTICATE_WANT) {
+
+		g_assert (GCK_IS_MODULE (auth->module));
+		g_assert (GCK_IS_OBJECT (auth->object));
+
+		g_free (auth->password);
+		auth->password = NULL;
+
+		if (_gck_module_fire_authenticate_object (auth->module, auth->object, auth->label, &auth->password)) {
+			auth->state = AUTHENTICATE_PERFORM;
+			return FALSE; /* Want to continue processing this call */
+		}
+	}
+
+	/* Free up various memory */
+	if (auth->module)
+		g_object_unref (auth->module);
+	if (auth->object)
+		g_object_unref (auth->object);
+	g_free (auth->label);
+	g_free (auth->password);
+
+	/* The call is complete */
+	return TRUE;
+}
+
+static void
+authenticate_init (Authenticate *auth, GckSlot *slot, GckObject *object)
+{
+	GckModule *module;
+
+	g_assert (GCK_IS_SLOT (slot));
+	g_assert (GCK_IS_OBJECT (object));
+
+	module = gck_slot_get_module (slot);
+	if (gck_module_get_auto_authenticate (module) & GCK_AUTHENTICATE_OBJECTS) {
+		auth->state = AUTHENTICATE_CAN;
+		auth->protected_auth = gck_slot_has_flags (slot, CKF_PROTECTED_AUTHENTICATION_PATH);
+		auth->module = module;
+		auth->object = g_object_ref (object);
+	} else {
+		auth->state = AUTHENTICATE_NONE;
+		g_object_unref (module);
+	}
+}
+
+/* --------------------------------------------------------------------------------------------------
+ * COMMON CRYPTO ROUTINES
+ */
+
+typedef struct _Crypt {
+	GckArguments base;
+
+	/* Authenticator */
+	Authenticate auth;
+
+	/* Functions to call */
+	CK_C_EncryptInit init_func;
+	CK_C_Encrypt complete_func;
+
+	/* Input */
+	CK_OBJECT_HANDLE key;
+	GckMechanism *mech;
+	guchar *input;
+	CK_ULONG n_input;
+
+	/* Output */
+	guchar *result;
+	CK_ULONG n_result;
+
+} Crypt;
+
+static CK_RV
+perform_crypt (Crypt *args)
+{
+	CK_RV rv;
+
+	g_assert (args);
+	g_assert (args->init_func);
+	g_assert (args->complete_func);
+	g_assert (!args->result);
+	g_assert (!args->n_result);
+
+	/* Initialize the crypt operation */
+	rv = (args->init_func) (args->base.handle, (CK_MECHANISM_PTR)args->mech, args->key);
+	if (rv != CKR_OK)
+		return rv;
+
+	rv = authenticate_perform (&args->auth, &args->base);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* Get the length of the result */
+	rv = (args->complete_func) (args->base.handle, args->input, args->n_input, NULL, &args->n_result);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* And try again with a real buffer */
+	args->result = g_malloc0 (args->n_result);
+	return (args->complete_func) (args->base.handle, args->input, args->n_input, args->result, &args->n_result);
+}
+
+static gboolean
+complete_crypt (Crypt *args, CK_RV result)
+{
+	if (!authenticate_complete (&args->auth, &args->base, result))
+		return FALSE;
+
+	/* Call is complete */
+	return TRUE;
+}
+
+static void
+free_crypt (Crypt *args)
+{
+	g_free (args->input);
+	gck_mechanism_unref (args->mech);
+	g_free (args->result);
+	g_free (args);
+}
+
+static guchar*
+crypt_sync (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input,
+            gsize n_input, gsize *n_result, GCancellable *cancellable, GError **err,
+            CK_C_EncryptInit init_func, CK_C_Encrypt complete_func)
+{
+	Crypt args;
+	GckSlot *slot;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (key), NULL);
+	g_return_val_if_fail (mechanism, NULL);
+	g_return_val_if_fail (init_func, NULL);
+	g_return_val_if_fail (complete_func, NULL);
+
+	memset (&args, 0, sizeof (args));
+	g_object_get (key, "handle", &args.key, NULL);
+	g_return_val_if_fail (args.key != 0, NULL);
+
+	args.mech = mechanism;
+
+	/* No need to copy in this case */
+	args.input = (guchar*)input;
+	args.n_input = n_input;
+
+	args.init_func = init_func;
+	args.complete_func = complete_func;
+
+	slot = gck_session_get_slot (self);
+	authenticate_init (&args.auth, slot, key);
+	g_object_unref (slot);
+
+	if (!_gck_call_sync (self, perform_crypt, complete_crypt, &args, cancellable, err)) {
+		g_free (args.result);
+		return NULL;
+	}
+
+	*n_result = args.n_result;
+	return args.result;
+}
+
+static void
+crypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism, const guchar *input,
+             gsize n_input, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data,
+             CK_C_EncryptInit init_func, CK_C_Encrypt complete_func)
+{
+	Crypt *args = _gck_call_async_prep (self, self, perform_crypt, complete_crypt, sizeof (*args), free_crypt);
+	GckSlot *slot;
+
+	g_return_if_fail (GCK_IS_OBJECT (key));
+	g_return_if_fail (mechanism);
+	g_return_if_fail (init_func);
+	g_return_if_fail (complete_func);
+
+	g_object_get (key, "handle", &args->key, NULL);
+	g_return_if_fail (args->key != 0);
+
+	args->mech = gck_mechanism_ref (mechanism);
+
+	args->input = input && n_input ? g_memdup (input, n_input) : NULL;
+	args->n_input = n_input;
+
+	args->init_func = init_func;
+	args->complete_func = complete_func;
+
+	slot = gck_session_get_slot (self);
+	authenticate_init (&args->auth, slot, key);
+	g_object_unref (slot);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+static guchar*
+crypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result, GError **err)
+{
+	Crypt *args;
+	guchar *res;
+
+	if (!_gck_call_basic_finish (result, err))
+		return NULL;
+	args = _gck_call_arguments (result, Crypt);
+
+	/* Steal the values from the results */
+	res = args->result;
+	args->result = NULL;
+	*n_result = args->n_result;
+	args->n_result = 0;
+
+	return res;
+}
+
+/* --------------------------------------------------------------------------------------------------
+ * ENCRYPT
+ */
+
+/**
+ * gck_session_encrypt:
+ * @self: The session.
+ * @key: The key to encrypt with.
+ * @mech_type: The mechanism type to use for encryption.
+ * @input: The data to encrypt.
+ * @n_input: The length of the data to encrypt.
+ * @n_result: A location to store the length of the result data.
+ * @err: A location to place error information.
+ *
+ * Encrypt data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: The data that was encrypted, or NULL if an error occured.
+ */
+guchar*
+gck_session_encrypt (GckSession *self, GckObject *key, gulong mech_type, const guchar *input,
+                      gsize n_input, gsize *n_result, GError **err)
+{
+	GckMechanism mechanism = { mech_type, NULL, 0 };
+	return gck_session_encrypt_full (self, key, &mechanism, input, n_input, n_result, NULL, err);
+}
+
+/**
+ * gck_session_encrypt_full:
+ * @self: The session.
+ * @key: The key to encrypt with.
+ * @mechanism: The mechanism type and parameters to use for encryption.
+ * @input: The data to encrypt.
+ * @n_input: The length of the data to encrypt.
+ * @n_result: A location to store the length of the result data.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @err: A location to place error information.
+ *
+ * Encrypt data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: The data that was encrypted, or NULL if an error occured.
+ */
+guchar*
+gck_session_encrypt_full (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                           const guchar *input, gsize n_input, gsize *n_result,
+                           GCancellable *cancellable, GError **err)
+{
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+	guchar *ret;
+
+	g_object_get (self, "module", &module, NULL);
+	g_return_val_if_fail (module != NULL, NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (module != NULL, NULL);
+
+	ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, err,
+	                  funcs->C_EncryptInit, funcs->C_Encrypt);
+
+	g_object_unref (module);
+	return ret;
+}
+
+/**
+ * gck_session_encrypt_async:
+ * @self: The session.
+ * @key: The key to encrypt with.
+ * @mechanism: The mechanism type and parameters to use for encryption.
+ * @input: The data to encrypt.
+ * @n_input: The length of the data to encrypt.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @callback: Called when the operation completes.
+ * @user_data: A pointer to pass to the callback.
+ *
+ * Encrypt data in a mechanism specific manner. This call will
+ * return immediately and complete asynchronously.
+ **/
+void
+gck_session_encrypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                            const guchar *input, gsize n_input, GCancellable *cancellable,
+                            GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+
+	g_object_get (self, "module", &module, NULL);
+	g_return_if_fail (module != NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_if_fail (module != NULL);
+
+	crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data,
+	             funcs->C_EncryptInit, funcs->C_Encrypt);
+
+	g_object_unref (module);
+}
+
+/**
+ * gck_session_encrypt_finish:
+ * @self: The session.
+ * @result: The result object passed to the callback.
+ * @n_result: A location to store the length of the result data.
+ * @err: A location to place error information.
+ *
+ * Get the result of an encryption operation.
+ *
+ * Returns: The data that was encrypted, or NULL if an error occurred.
+ */
+guchar*
+gck_session_encrypt_finish (GckSession *self, GAsyncResult *result, gsize *n_result,
+                             GError **err)
+{
+	return crypt_finish (self, result, n_result, err);
+}
+
+/* --------------------------------------------------------------------------------------------------
+ * DECRYPT
+ */
+
+/**
+ * gck_session_decrypt:
+ * @self: The session.
+ * @key: The key to decrypt with.
+ * @mech_type: The mechanism type to use for decryption.
+ * @input: The data to decrypt.
+ * @n_input: The length of the data to decrypt.
+ * @n_result: A location to store the length of the result data.
+ * @err: A location to place an error.
+ *
+ * Decrypt data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: The data that was decrypted, or NULL if an error occured.
+ */
+guchar*
+gck_session_decrypt (GckSession *self, GckObject *key, gulong mech_type, const guchar *input,
+                      gsize n_input, gsize *n_result, GError **err)
+{
+	GckMechanism mechanism = { mech_type, NULL, 0 };
+	return gck_session_decrypt_full (self, key, &mechanism, input, n_input, n_result, NULL, err);
+}
+
+/**
+ * gck_session_decrypt_full:
+ * @self: The session.
+ * @key: The key to decrypt with.
+ * @mechanism: The mechanism type and parameters to use for decryption.
+ * @input: The data to decrypt.
+ * @n_input: The length of the data to decrypt.
+ * @n_result: A location to store the length of the result data.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @err: A location to place error information.
+ *
+ * Decrypt data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: The data that was decrypted, or NULL if an error occured.
+ */
+guchar*
+gck_session_decrypt_full (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                           const guchar *input, gsize n_input, gsize *n_result,
+                           GCancellable *cancellable, GError **err)
+{
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+	guchar *ret;
+
+	g_object_get (self, "module", &module, NULL);
+	g_return_val_if_fail (module != NULL, NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (module != NULL, NULL);
+
+	ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, err,
+	                  funcs->C_DecryptInit, funcs->C_Decrypt);
+	g_object_unref (module);
+	return ret;
+}
+
+/**
+ * gck_session_decrypt_async:
+ * @self: The session.
+ * @key: The key to decrypt with.
+ * @mechanism: The mechanism type and parameters to use for decryption.
+ * @input: The data to decrypt.
+ * @n_input: The length of the data to decrypt.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @callback: Called when the operation completes.
+ * @user_data: A pointer to pass to the callback.
+ *
+ * Decrypt data in a mechanism specific manner. This call will
+ * return immediately and complete asynchronously.
+ */
+void
+gck_session_decrypt_async (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                            const guchar *input, gsize n_input, GCancellable *cancellable,
+                            GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+
+	g_object_get (self, "module", &module, NULL);
+	g_return_if_fail (module != NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_if_fail (module != NULL);
+
+	crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data,
+	             funcs->C_DecryptInit, funcs->C_Decrypt);
+	g_object_unref (module);
+}
+
+/**
+ * gck_session_decrypt_finish:
+ * @self: The session.
+ * @result: The result object passed to the callback.
+ * @n_result: A location to store the length of the result data.
+ * @err: A location to place error information.
+ *
+ * Get the result of an decryption operation.
+ *
+ * Returns: The data that was decrypted, or NULL if an error occurred.
+ */
+guchar*
+gck_session_decrypt_finish (GckSession *self, GAsyncResult *result,
+                             gsize *n_result, GError **err)
+{
+	return crypt_finish (self, result, n_result, err);
+}
+
+/* --------------------------------------------------------------------------------------------------
+ * SIGN
+ */
+
+/**
+ * gck_session_sign:
+ * @self: The session.
+ * @key: The key to sign with.
+ * @mech_type: The mechanism type to use for signing.
+ * @input: The data to sign.
+ * @n_input: The length of the data to sign.
+ * @n_result: A location to store the length of the result data.
+ * @err: A location to place an error.
+ *
+ * Sign data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: The data that was signed, or NULL if an error occured.
+ */
+guchar*
+gck_session_sign (GckSession *self, GckObject *key, gulong mech_type, const guchar *input,
+                   gsize n_input, gsize *n_result, GError **err)
+{
+	GckMechanism mechanism = { mech_type, NULL, 0 };
+	return gck_session_sign_full (self, key, &mechanism, input, n_input, n_result, NULL, err);
+}
+
+/**
+ * gck_session_sign_full:
+ * @self: The session.
+ * @key: The key to sign with.
+ * @mechanism: The mechanism type and parameters to use for signing.
+ * @input: The data to sign.
+ * @n_input: The length of the data to sign.
+ * @n_result: A location to store the length of the result data.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @err: A location to place error information.
+ *
+ * Sign data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: The data that was signed, or NULL if an error occured.
+ */
+guchar*
+gck_session_sign_full (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                        const guchar *input, gsize n_input, gsize *n_result,
+                        GCancellable *cancellable, GError **err)
+{
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+	guchar *ret;
+
+	g_object_get (self, "module", &module, NULL);
+	g_return_val_if_fail (module != NULL, NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (module != NULL, NULL);
+
+	ret = crypt_sync (self, key, mechanism, input, n_input, n_result, cancellable, err,
+	                  funcs->C_SignInit, funcs->C_Sign);
+	g_object_unref (module);
+	return ret;
+}
+
+/**
+ * gck_session_sign_async:
+ * @self: The session.
+ * @key: The key to sign with.
+ * @mechanism: The mechanism type and parameters to use for signing.
+ * @input: The data to sign.
+ * @n_input: The length of the data to sign.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @callback: Called when the operation completes.
+ * @user_data: A pointer to pass to the callback.
+ *
+ * Sign data in a mechanism specific manner. This call will
+ * return immediately and complete asynchronously.
+ */
+void
+gck_session_sign_async (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                         const guchar *input, gsize n_input, GCancellable *cancellable,
+                         GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+
+	g_object_get (self, "module", &module, NULL);
+	g_return_if_fail (module != NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_if_fail (module != NULL);
+
+	crypt_async (self, key, mechanism, input, n_input, cancellable, callback, user_data,
+	             funcs->C_SignInit, funcs->C_Sign);
+	g_object_unref (module);
+}
+
+/**
+ * gck_session_sign_finish:
+ * @self: The session.
+ * @result: The result object passed to the callback.
+ * @n_result: A location to store the length of the result data.
+ * @err: A location to place error information.
+ *
+ * Get the result of an signing operation.
+ *
+ * Returns: The data that was signed, or NULL if an error occurred.
+ */
+guchar*
+gck_session_sign_finish (GckSession *self, GAsyncResult *result,
+                          gsize *n_result, GError **err)
+{
+	return crypt_finish (self, result, n_result, err);
+}
+
+/* --------------------------------------------------------------------------------------------------
+ * VERIFY
+ */
+
+typedef struct _Verify {
+	GckArguments base;
+
+	/* Authenticator */
+	Authenticate auth;
+
+	/* Input */
+	CK_OBJECT_HANDLE key;
+	GckMechanism *mech;
+	guchar *input;
+	CK_ULONG n_input;
+	guchar *signature;
+	CK_ULONG n_signature;
+
+} Verify;
+
+static CK_RV
+perform_verify (Verify *args)
+{
+	CK_RV rv;
+
+	/* Initialize the crypt operation */
+	rv = (args->base.pkcs11->C_VerifyInit) (args->base.handle, (CK_MECHANISM_PTR)args->mech, args->key);
+	if (rv != CKR_OK)
+		return rv;
+
+	rv = authenticate_perform (&args->auth, &args->base);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* Do the actual verify */
+	return (args->base.pkcs11->C_Verify) (args->base.handle, args->input, args->n_input,
+	                                      args->signature, args->n_signature);
+}
+
+static gboolean
+complete_verify (Verify *args, CK_RV result)
+{
+	if (!authenticate_complete (&args->auth, &args->base, result))
+		return FALSE;
+
+	/* Call is complete */
+	return TRUE;
+}
+
+static void
+free_verify (Verify *args)
+{
+	g_free (args->input);
+	g_free (args->signature);
+	gck_mechanism_unref (args->mech);
+	g_free (args);
+}
+
+/**
+ * gck_session_verify:
+ * @self: The session.
+ * @key: The key to verify with.
+ * @mech_type: The mechanism type to use for verifying.
+ * @input: The data to verify.
+ * @n_input: The length of the data to verify.
+ * @signature: The signature.
+ * @n_signature: The length of the signature.
+ * @err: A location to place an error.
+ *
+ * Verify data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: TRUE if the data verified correctly, otherwise a failure or error occurred.
+ */
+gboolean
+gck_session_verify (GckSession *self, GckObject *key, gulong mech_type, const guchar *input,
+                     gsize n_input, const guchar *signature, gsize n_signature, GError **err)
+{
+	GckMechanism mechanism = { mech_type, NULL, 0 };
+	return gck_session_verify_full (self, key, &mechanism, input, n_input,
+	                                 signature, n_signature, NULL, err);
+}
+
+/**
+ * gck_session_verify_full:
+ * @self: The session.
+ * @key: The key to verify with.
+ * @mechanism: The mechanism type and parameters to use for signing.
+ * @input: The data to verify.
+ * @n_input: The length of the data to verify.
+ * @signature: The signature.
+ * @n_signature: The length of the signature.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @err: A location to place an error.
+ *
+ * Verify data in a mechanism specific manner. This call may
+ * block for an indefinite period.
+ *
+ * Returns: TRUE if the data verified correctly, otherwise a failure or error occurred.
+ */
+gboolean
+gck_session_verify_full (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                          const guchar *input, gsize n_input, const guchar *signature,
+                          gsize n_signature, GCancellable *cancellable, GError **err)
+{
+	Verify args;
+	GckSlot *slot;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (key), FALSE);
+	g_return_val_if_fail (mechanism, FALSE);
+
+	memset (&args, 0, sizeof (args));
+	g_object_get (key, "handle", &args.key, NULL);
+	g_return_val_if_fail (args.key != 0, FALSE);
+
+	args.mech = mechanism;
+
+	/* No need to copy in this case */
+	args.input = (guchar*)input;
+	args.n_input = n_input;
+	args.signature = (guchar*)signature;
+	args.n_signature = n_signature;
+
+	slot = gck_session_get_slot (self);
+	authenticate_init (&args.auth, slot, key);
+	g_object_unref (slot);
+
+	return _gck_call_sync (self, perform_verify, complete_verify, &args, cancellable, err);
+}
+
+/**
+ * gck_session_verify_async:
+ * @self: The session.
+ * @key: The key to verify with.
+ * @mechanism: The mechanism type and parameters to use for signing.
+ * @input: The data to verify.
+ * @n_input: The length of the data to verify.
+ * @signature: The signature.
+ * @n_signature: The length of the signature.
+ * @cancellable: A GCancellable which can be used to cancel the operation.
+ * @callback: Called when the operation completes.
+ * @user_data: A pointer to pass to the callback.
+ *
+ * Verify data in a mechanism specific manner. This call returns
+ * immediately and completes asynchronously.
+ */
+void
+gck_session_verify_async (GckSession *self, GckObject *key, GckMechanism *mechanism,
+                           const guchar *input, gsize n_input, const guchar *signature,
+                           gsize n_signature, GCancellable *cancellable,
+                           GAsyncReadyCallback callback, gpointer user_data)
+{
+	Verify *args = _gck_call_async_prep (self, self, perform_verify, complete_verify, sizeof (*args), free_verify);
+	GckSlot *slot;
+
+	g_return_if_fail (GCK_IS_OBJECT (key));
+	g_return_if_fail (mechanism);
+
+	g_object_get (key, "handle", &args->key, NULL);
+	g_return_if_fail (args->key != 0);
+
+	args->mech = gck_mechanism_ref (mechanism);
+
+	args->input = input && n_input ? g_memdup (input, n_input) : NULL;
+	args->n_input = n_input;
+	args->signature = signature && n_signature ? g_memdup (signature, n_signature) : NULL;
+	args->n_signature = n_signature;
+
+	slot = gck_session_get_slot (self);
+	authenticate_init (&args->auth, slot, key);
+	g_object_unref (slot);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_verify_finish:
+ * @self: The session.
+ * @result: The result object passed to the callback.
+ * @err: A location to place error information.
+ *
+ * Get the result of an verify operation.
+ *
+ * Returns: TRUE if the data verified correctly, otherwise a failure or error occurred.
+ */
+gboolean
+gck_session_verify_finish (GckSession *self, GAsyncResult *result, GError **err)
+{
+	return _gck_call_basic_finish (result, err);
+}
diff --git a/gck/gck-slot.c b/gck/gck-slot.c
new file mode 100644
index 0000000..e4092bf
--- /dev/null
+++ b/gck/gck-slot.c
@@ -0,0 +1,1061 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-slot.c - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck.h"
+#include "gck-private.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gck-slot
+ * @title: GckSlot
+ * @short_description: Represents a PKCS11 slot that can contain a token.
+ *
+ * A PKCS11 slot can contain a token. As an example, a slot might be a card reader, and the token
+ * the card. If the PKCS11 module is not a hardware driver, often the slot and token are equivalent.
+ */
+
+/**
+ * GckSlot:
+ *
+ * Represents a PKCS11 slot.
+ */
+
+enum {
+	PROP_0,
+	PROP_MODULE,
+	PROP_HANDLE
+};
+
+typedef struct _GckSlotData {
+	GckModule *module;
+	CK_SLOT_ID handle;
+} GckSlotData;
+
+typedef struct _GckSlotPrivate {
+	GckSlotData data;
+} GckSlotPrivate;
+
+G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT);
+
+#define GCK_SLOT_GET_DATA(o) \
+      (G_TYPE_INSTANCE_GET_PRIVATE((o), GCK_TYPE_SLOT, GckSlotData))
+
+#ifndef HAVE_TIMEGM
+
+time_t
+timegm(struct tm *t)
+{
+	time_t tl, tb;
+	struct tm *tg;
+
+	tl = mktime (t);
+	if (tl == -1)
+	{
+		t->tm_hour--;
+		tl = mktime (t);
+		if (tl == -1)
+			return -1;
+		tl += 3600;
+	    }
+	tg = gmtime (&tl);
+	tg->tm_isdst = 0;
+	tb = mktime (tg);
+	if (tb == -1)
+	{
+		tg->tm_hour--;
+		tb = mktime (tg);
+		if (tb == -1)
+			return -1;
+		tb += 3600;
+	}
+	return (tl - (tb - tl));
+}
+
+#endif
+
+/* ----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+static GckSession*
+make_session_object (GckSlot *self, gulong flags, CK_SESSION_HANDLE handle)
+{
+	GckSession *session;
+	GckModule *module;
+
+	g_return_val_if_fail (handle != 0, NULL);
+
+	module = gck_slot_get_module (self);
+
+	session = gck_session_from_handle (self, handle);
+	g_return_val_if_fail (session != NULL, NULL);
+
+	/* Session keeps a reference to module so this is safe */
+	g_signal_connect (session, "discard-handle",
+	                  G_CALLBACK (_gck_module_pool_session_handle), module);
+
+	g_object_unref (module);
+
+	return session;
+}
+
+/* ----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_slot_init (GckSlot *self)
+{
+
+}
+
+static void
+gck_slot_get_property (GObject *obj, guint prop_id, GValue *value,
+                        GParamSpec *pspec)
+{
+	GckSlot *self = GCK_SLOT (obj);
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_value_take_object (value, gck_slot_get_module (self));
+		break;
+	case PROP_HANDLE:
+		g_value_set_ulong (value, gck_slot_get_handle (self));
+		break;
+	}
+}
+
+static void
+gck_slot_set_property (GObject *obj, guint prop_id, const GValue *value,
+                        GParamSpec *pspec)
+{
+	GckSlotData *data = GCK_SLOT_GET_DATA (obj);
+
+	/* All writes to data members below, happen only during construct phase */
+
+	switch (prop_id) {
+	case PROP_MODULE:
+		g_assert (!data->module);
+		data->module = g_value_get_object (value);
+		g_assert (data->module);
+		g_object_ref (data->module);
+		break;
+	case PROP_HANDLE:
+		g_assert (!data->handle);
+		data->handle = g_value_get_ulong (value);
+		break;
+	}
+}
+
+static void
+gck_slot_dispose (GObject *obj)
+{
+	G_OBJECT_CLASS (gck_slot_parent_class)->dispose (obj);
+}
+
+static void
+gck_slot_finalize (GObject *obj)
+{
+	GckSlotData *data = GCK_SLOT_GET_DATA (obj);
+
+	data->handle = 0;
+
+	if (data->module)
+		g_object_unref (data->module);
+	data->module = NULL;
+
+	G_OBJECT_CLASS (gck_slot_parent_class)->finalize (obj);
+}
+
+
+static void
+gck_slot_class_init (GckSlotClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	gck_slot_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->get_property = gck_slot_get_property;
+	gobject_class->set_property = gck_slot_set_property;
+	gobject_class->dispose = gck_slot_dispose;
+	gobject_class->finalize = gck_slot_finalize;
+
+	/**
+	 * GckSlot:module:
+	 *
+	 * The PKCS11 object that this slot is a part of.
+	 */
+	g_object_class_install_property (gobject_class, PROP_MODULE,
+		g_param_spec_object ("module", "Module", "PKCS11 Module",
+		                     GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckSlot:handle:
+	 *
+	 * The raw CK_SLOT_ID handle of this slot.
+	 */
+	g_object_class_install_property (gobject_class, PROP_HANDLE,
+		g_param_spec_ulong ("handle", "Handle", "PKCS11 Slot ID",
+		                   0, G_MAXULONG, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (gobject_class, sizeof (GckSlotPrivate));
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+/**
+ * GckSlotInfo:
+ * @slot_description: Description of the slot.
+ * @manufacturer_id: The manufacturer of this slot.
+ * @flags: Various PKCS11 flags that apply to this slot.
+ * @hardware_version_major: The major version of the hardware.
+ * @hardware_version_minor: The minor version of the hardware.
+ * @firmware_version_major: The major version of the firmware.
+ * @firmware_version_minor: The minor version of the firmware.
+ *
+ * Represents information about a PKCS11 slot.
+ *
+ * This is analogous to a CK_SLOT_INFO structure, but the
+ * strings are far more usable.
+ *
+ * When you're done with this structure it should be released with
+ * gck_slot_info_free().
+ */
+
+/**
+ * gck_slot_info_free:
+ * @slot_info: The slot info to free, or NULL.
+ *
+ * Free the GckSlotInfo and associated resources.
+ **/
+void
+gck_slot_info_free (GckSlotInfo *slot_info)
+{
+	if (!slot_info)
+		return;
+	g_free (slot_info->slot_description);
+	g_free (slot_info->manufacturer_id);
+	g_free (slot_info);
+}
+
+/**
+ * GckTokenInfo:
+ * @label: The displayable token label.
+ * @manufacturer_id: The manufacturer of this slot.
+ * @model: The token model number as a string.
+ * @serial_number: The token serial number as a string.
+ * @flags: Various PKCS11 flags that apply to this token.
+ * @max_session_count: The maximum number of sessions allowed on this token.
+ * @session_count: The number of sessions open on this token.
+ * @max_rw_session_count: The maximum number of read/write sessions allowed on this token.
+ * @rw_session_count: The number of sessions open on this token.
+ * @max_pin_len: The maximum length of a PIN for locking this token.
+ * @min_pin_len: The minimum length of a PIN for locking this token.
+ * @total_public_memory: The total amount of memory on this token for storing public objects.
+ * @free_public_memory: The available amount of memory on this token for storing public objects.
+ * @total_private_memory: The total amount of memory on this token for storing private objects.
+ * @free_private_memory: The available amount of memory on this token for storing private objects.
+ * @hardware_version_major: The major version of the hardware.
+ * @hardware_version_minor: The minor version of the hardware.
+ * @firmware_version_major: The major version of the firmware.
+ * @firmware_version_minor: The minor version of the firmware.
+ * @utc_time: If the token has a hardware clock, this is set to the number of seconds since the epoch.
+ *
+ * Represents information about a PKCS11 token.
+ *
+ * This is analogous to a CK_TOKEN_INFO structure, but the
+ * strings are far more usable.
+ *
+ * When you're done with this structure it should be released with
+ * gck_token_info_free().
+ */
+
+/**
+ * gck_token_info_free:
+ * @token_info: The token info to free, or NULL.
+ *
+ * Free the GckTokenInfo and associated resources.
+ **/
+void
+gck_token_info_free (GckTokenInfo *token_info)
+{
+	if (!token_info)
+		return;
+	g_free (token_info->label);
+	g_free (token_info->manufacturer_id);
+	g_free (token_info->model);
+	g_free (token_info->serial_number);
+	g_free (token_info);
+}
+
+/**
+ * GckMechanismInfo:
+ * @min_key_size: The minimum key size that can be used with this mechanism.
+ * @max_key_size: The maximum key size that can be used with this mechanism.
+ * @flags: Various PKCS11 flags that apply to this mechanism.
+ *
+ * Represents information about a PKCS11 mechanism.
+ *
+ * This is analogous to a CK_MECHANISM_INFO structure.
+ *
+ * When you're done with this structure it should be released with
+ * gck_mechanism_info_free().
+ */
+
+/**
+ * gck_mechanism_info_free:
+ * @mech_info: The mechanism info to free, or NULL.
+ *
+ * Free the GckMechanismInfo and associated resources.
+ **/
+void
+gck_mechanism_info_free (GckMechanismInfo *mech_info)
+{
+	if (!mech_info)
+		return;
+	g_free (mech_info);
+}
+
+/**
+ * GckMechanisms:
+ *
+ * A set of GckMechanismInfo structures.
+ */
+
+/**
+ * gck_mechanisms_length:
+ * @a: A GckMechanisms set.
+ *
+ * Get the number of GckMechanismInfo in the set.
+ *
+ * Returns: The number in the set.
+ */
+
+/**
+ * gck_mechanisms_at:
+ * @a: A GckMechanisms set.
+ * @i: The index of a GckMechanismInfo.
+ *
+ * Get a specific GckMechanismInfo in a the set.
+ *
+ * Returns: The GckMechanismInfo.
+ */
+
+/**
+ * gck_mechanisms_free:
+ * @a: A GckMechanism set.
+ *
+ * Free a GckMechanisms set.
+ */
+
+/**
+ * gck_mechanisms_check:
+ * @mechanisms: A list of mechanisms, perhaps retrieved from gck_slot_get_mechanisms().
+ * @...: A list of mechanism types followed by GCK_INVALID.
+ *
+ * Check whether all the mechanism types are in the list.
+ *
+ * The arguments should be a list of CKM_XXX mechanism types. The last argument
+ * should be GCK_INVALID.
+ *
+ * Return value: Whether the mechanism is in the list or not.
+ **/
+gboolean
+gck_mechanisms_check (GckMechanisms *mechanisms, ...)
+{
+	gboolean found = TRUE;
+	va_list va;
+	gulong mech;
+	gsize i;
+
+	g_return_val_if_fail (mechanisms, FALSE);
+
+	va_start (va, mechanisms);
+	for (;;) {
+		mech = va_arg (va, gulong);
+		if (mech == GCK_INVALID)
+			break;
+
+		found = FALSE;
+		for (i = 0; i < gck_mechanisms_length (mechanisms); ++i) {
+			if (gck_mechanisms_at (mechanisms, i) == mech) {
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (found == FALSE)
+			break;
+
+	}
+	va_end (va);
+
+	return found;
+}
+
+/**
+ * gck_slot_equal:
+ * @slot1: A pointer to the first GckSlot
+ * @slot2: A pointer to the second GckSlot
+ *
+ * Checks equality of two slots. Two GckSlot objects can point to the same
+ * underlying PKCS#11 slot.
+ *
+ * Return value: TRUE if slot1 and slot2 are equal. FALSE if either is not a GckSlot.
+ **/
+gboolean
+gck_slot_equal (gconstpointer slot1, gconstpointer slot2)
+{
+	GckSlotData *data1, *data2;
+
+	if (slot1 == slot2)
+		return TRUE;
+	if (!GCK_IS_SLOT (slot1) || !GCK_IS_SLOT (slot2))
+		return FALSE;
+
+	data1 = GCK_SLOT_GET_DATA (slot1);
+	data2 = GCK_SLOT_GET_DATA (slot2);
+
+	return data1->handle == data2->handle &&
+	       gck_module_equal (data1->module, data2->module);
+}
+
+/**
+ * gck_slot_hash:
+ * @slot: A pointer to a GckSlot
+ *
+ * Create a hash value for the GckSlot.
+ *
+ * This function is intended for easily hashing a GckSlot to add to
+ * a GHashTable or similar data structure.
+ *
+ * Return value: An integer that can be used as a hash value, or 0 if invalid.
+ **/
+guint
+gck_slot_hash (gconstpointer slot)
+{
+	GckSlotData *data;
+
+	g_return_val_if_fail (GCK_IS_SLOT (slot), 0);
+
+	data = GCK_SLOT_GET_DATA (slot);
+
+	return _gck_ulong_hash (&data->handle) ^
+	       gck_module_hash (data->module);
+}
+
+/**
+ * gck_slot_get_handle:
+ * @self: The slot to get the handle of.
+ *
+ * Get the raw PKCS#11 handle of a slot.
+ *
+ * Return value: The raw handle.
+ **/
+CK_SLOT_ID
+gck_slot_get_handle (GckSlot *self)
+{
+	GckSlotData *data = GCK_SLOT_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_SLOT (self), (CK_SLOT_ID)-1);
+	return data->handle;
+}
+
+/**
+ * gck_slot_get_module:
+ * @self: The slot to get the module for.
+ *
+ * Get the module that this slot is on.
+ *
+ * Return value: The module, you must unreference this after you're done with it.
+ */
+GckModule*
+gck_slot_get_module (GckSlot *self)
+{
+	GckSlotData *data = GCK_SLOT_GET_DATA (self);
+	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (data->module), NULL);
+	return g_object_ref (data->module);
+}
+
+/**
+ * gck_slot_get_info:
+ * @self: The slot to get info for.
+ *
+ * Get the information for this slot.
+ *
+ * Return value: The slot information. When done, use gck_slot_info_free()
+ * to release it.
+ **/
+GckSlotInfo*
+gck_slot_get_info (GckSlot *self)
+{
+	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
+	GckModule *module = NULL;
+	CK_FUNCTION_LIST_PTR funcs;
+	GckSlotInfo *slotinfo;
+	CK_SLOT_INFO info;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
+
+	g_object_get (self, "module", &module, "handle", &handle, NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (funcs, NULL);
+
+	memset (&info, 0, sizeof (info));
+	rv = (funcs->C_GetSlotInfo) (handle, &info);
+
+	g_object_unref (module);
+
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv));
+		return NULL;
+	}
+
+	slotinfo = g_new0 (GckSlotInfo, 1);
+	slotinfo->slot_description = gck_string_from_chars (info.slotDescription,
+	                                                     sizeof (info.slotDescription));
+	slotinfo->manufacturer_id = gck_string_from_chars (info.manufacturerID,
+	                                                    sizeof (info.manufacturerID));
+	slotinfo->flags = info.flags;
+	slotinfo->hardware_version_major = info.hardwareVersion.major;
+	slotinfo->hardware_version_minor = info.hardwareVersion.minor;
+	slotinfo->firmware_version_major = info.firmwareVersion.major;
+	slotinfo->firmware_version_minor = info.firmwareVersion.minor;
+
+	return slotinfo;
+}
+
+/**
+ * gck_slot_get_token_info:
+ * @self: The slot to get info for.
+ *
+ * Get the token information for this slot.
+ *
+ * Return value: The token information. When done, use gck_token_info_free()
+ * to release it.
+ **/
+GckTokenInfo*
+gck_slot_get_token_info (GckSlot *self)
+{
+	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
+	CK_FUNCTION_LIST_PTR funcs;
+	GckModule *module = NULL;
+	GckTokenInfo *tokeninfo;
+	CK_TOKEN_INFO info;
+	gchar *string;
+	struct tm tm;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
+
+	g_object_get (self, "module", &module, "handle", &handle, NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (funcs, NULL);
+
+	memset (&info, 0, sizeof (info));
+	rv = (funcs->C_GetTokenInfo) (handle, &info);
+
+	g_object_unref (module);
+
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv));
+		return NULL;
+	}
+
+	tokeninfo = g_new0 (GckTokenInfo, 1);
+	tokeninfo->label = gck_string_from_chars (info.label, sizeof (info.label));
+	tokeninfo->model = gck_string_from_chars (info.model, sizeof (info.model));
+	tokeninfo->manufacturer_id = gck_string_from_chars (info.manufacturerID,
+	                                                     sizeof (info.manufacturerID));
+	tokeninfo->serial_number = gck_string_from_chars (info.serialNumber,
+	                                                   sizeof (info.serialNumber));
+	tokeninfo->flags = info.flags;
+	tokeninfo->max_session_count = info.ulMaxSessionCount;
+	tokeninfo->session_count = info.ulSessionCount;
+	tokeninfo->max_rw_session_count = info.ulMaxRwSessionCount;
+	tokeninfo->rw_session_count = info.ulRwSessionCount;
+	tokeninfo->max_pin_len = info.ulMaxPinLen;
+	tokeninfo->min_pin_len = info.ulMinPinLen;
+	tokeninfo->total_public_memory = info.ulTotalPublicMemory;
+	tokeninfo->total_private_memory = info.ulTotalPrivateMemory;
+	tokeninfo->free_private_memory = info.ulFreePrivateMemory;
+	tokeninfo->free_public_memory = info.ulFreePublicMemory;
+	tokeninfo->hardware_version_major = info.hardwareVersion.major;
+	tokeninfo->hardware_version_minor = info.hardwareVersion.minor;
+	tokeninfo->firmware_version_major = info.firmwareVersion.major;
+	tokeninfo->firmware_version_minor = info.firmwareVersion.minor;
+
+	/* Parse the time into seconds since epoch */
+	if (info.flags & CKF_CLOCK_ON_TOKEN) {
+		string = g_strndup ((gchar*)info.utcTime, MIN (14, sizeof (info.utcTime)));
+		if (!strptime (string, "%Y%m%d%H%M%S", &tm))
+			tokeninfo->utc_time = -1;
+		else
+			tokeninfo->utc_time = timegm (&tm);
+		g_free (string);
+	} else {
+		tokeninfo->utc_time = -1;
+	}
+
+	return tokeninfo;
+}
+
+/**
+ * gck_slot_get_mechanisms:
+ * @self: The slot to get mechanisms for.
+ *
+ * Get the available mechanisms for this slot.
+ *
+ * Return value: A list of the mechanisms for this slot. Use
+ * gck_mechanisms_free() when done with this.
+ **/
+GckMechanisms*
+gck_slot_get_mechanisms (GckSlot *self)
+{
+	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
+	CK_FUNCTION_LIST_PTR funcs;
+	GckModule *module = NULL;
+	CK_MECHANISM_TYPE_PTR mech_list = NULL;
+	CK_ULONG count, i;
+	GckMechanisms *result;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
+
+	g_object_get (self, "module", &module, "handle", &handle, NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (funcs, NULL);
+
+	rv = (funcs->C_GetMechanismList) (handle, NULL, &count);
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get mechanism count: %s", gck_message_from_rv (rv));
+		count = 0;
+	} else {
+		mech_list = g_new (CK_MECHANISM_TYPE, count);
+		rv = (funcs->C_GetMechanismList) (handle, mech_list, &count);
+		if (rv != CKR_OK) {
+			g_warning ("couldn't get mechanism list: %s", gck_message_from_rv (rv));
+			g_free (mech_list);
+			count = 0;
+		}
+	}
+
+	g_object_unref (module);
+
+	if (!count)
+		return NULL;
+
+	result = g_array_new (FALSE, TRUE, sizeof (CK_MECHANISM_TYPE));
+	for (i = 0; i < count; ++i)
+		g_array_append_val (result, mech_list[i]);
+
+	g_free (mech_list);
+	return result;
+
+}
+
+/**
+ * gck_slot_get_mechanism_info:
+ * @self: The slot to get mechanism info from.
+ * @mech_type: The mechanisms type to get info for.
+ *
+ * Get information for the specified mechanism.
+ *
+ * Return value: The mechanism information, or NULL if failed. Use
+ * gck_mechanism_info_free() when done with it.
+ **/
+GckMechanismInfo*
+gck_slot_get_mechanism_info (GckSlot *self, gulong mech_type)
+{
+	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
+	CK_FUNCTION_LIST_PTR funcs;
+	GckMechanismInfo *mechinfo;
+	GckModule *module = NULL;
+	CK_MECHANISM_INFO info;
+	struct tm;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
+
+	g_object_get (self, "module", &module, "handle", &handle, NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (funcs, NULL);
+
+	memset (&info, 0, sizeof (info));
+	rv = (funcs->C_GetMechanismInfo) (handle, mech_type, &info);
+
+	g_object_unref (module);
+
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get mechanism info: %s", gck_message_from_rv (rv));
+		return NULL;
+	}
+
+	mechinfo = g_new0 (GckMechanismInfo, 1);
+	mechinfo->flags = info.flags;
+	mechinfo->max_key_size = info.ulMaxKeySize;
+	mechinfo->min_key_size = info.ulMinKeySize;
+
+	return mechinfo;
+}
+
+/**
+ * gck_slot_has_flags:
+ * @self: The GckSlot object.
+ * @flags: The flags to check.
+ *
+ * Check if the PKCS11 slot has the given flags.
+ *
+ * Returns: Whether one or more flags exist.
+ */
+gboolean
+gck_slot_has_flags (GckSlot *self, gulong flags)
+{
+	CK_FUNCTION_LIST_PTR funcs;
+	GckModule *module = NULL;
+	CK_TOKEN_INFO info;
+	CK_SLOT_ID handle;
+	CK_RV rv;
+
+	g_return_val_if_fail (GCK_IS_SLOT (self), FALSE);
+
+	g_object_get (self, "module", &module, "handle", &handle, NULL);
+	g_return_val_if_fail (GCK_IS_MODULE (module), FALSE);
+
+	funcs = gck_module_get_functions (module);
+	g_return_val_if_fail (funcs, FALSE);
+
+	memset (&info, 0, sizeof (info));
+	rv = (funcs->C_GetTokenInfo) (handle, &info);
+
+	g_object_unref (module);
+
+	if (rv != CKR_OK) {
+		g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv));
+		return FALSE;
+	}
+
+	return (info.flags & flags) != 0;
+}
+
+#if UNIMPLEMENTED
+
+typedef struct InitToken {
+	GckArguments base;
+	const guchar *pin;
+	gsize length;
+	const gchar *label;
+} InitToken;
+
+static CK_RV
+perform_init_token (InitToken *args)
+{
+	return (args->base.pkcs11->C_InitToken) (args->base.handle,
+	                                         args->pin, args->length,
+	                                         args->label);
+}
+
+gboolean
+gck_slot_init_token (GckSlot *self, const guchar *pin, gsize length,
+                      const gchar *label, GCancellable *cancellable,
+                      GError **err)
+{
+	InitToken args = { GCK_ARGUMENTS_INIT, pin, length, label };
+	return _gck_call_sync (self, perform_init_token, NULL, &args, err);
+}
+
+void
+gck_slot_init_token_async (GckSlot *self, const guchar *pin, gsize length,
+                            const gchar *label, GCancellable *cancellable,
+                            GAsyncReadyCallback callback, gpointer user_data)
+{
+	InitToken* args = _gck_call_async_prep (self, self, perform_init_token,
+	                                         NULL, sizeof (*args));
+
+	args->pin = pin;
+	args->length = length;
+	args->label = label;
+
+	_gck_call_async_go (args, cancellable, callback, user_data);
+}
+
+gboolean
+gck_slot_init_token_finish (GckSlot *self, GAsyncResult *result, GError **err)
+{
+	return _gck_call_basic_finish (self, result, err);
+}
+
+#endif /* UNIMPLEMENTED */
+
+typedef struct OpenSession {
+	GckArguments base;
+	GckSlot *slot;
+	gulong flags;
+	gpointer app_data;
+	CK_NOTIFY notify;
+	gchar *password;
+	gboolean auto_login;
+	CK_SESSION_HANDLE session;
+} OpenSession;
+
+static CK_RV
+perform_open_session (OpenSession *args)
+{
+	CK_SESSION_INFO info;
+	CK_RV rv = CKR_OK;
+	CK_ULONG pin_len;
+
+	/* Can be called multiple times */
+
+	/* First step, open session */
+	if (!args->session) {
+		rv = (args->base.pkcs11->C_OpenSession) (args->base.handle, args->flags,
+		                                         args->app_data, args->notify, &args->session);
+	}
+
+	if (rv != CKR_OK || !args->auto_login)
+		return rv;
+
+	/* Step two, check if session is logged in */
+	rv = (args->base.pkcs11->C_GetSessionInfo) (args->session, &info);
+	if (rv != CKR_OK)
+		return rv;
+
+	/* Already logged in? */
+	if (info.state != CKS_RO_PUBLIC_SESSION && info.state != CKS_RW_PUBLIC_SESSION)
+		return CKR_OK;
+
+	/* Try to login */
+	pin_len = args->password ? strlen (args->password) : 0;
+	return (args->base.pkcs11->C_Login) (args->session, CKU_USER,
+	                                     (CK_UTF8CHAR_PTR)args->password, pin_len);
+}
+
+static gboolean
+complete_open_session (OpenSession *args, CK_RV result)
+{
+	GckModule *module;
+	gboolean ret = TRUE;
+
+	g_free (args->password);
+	args->password = NULL;
+
+	/* Ask the token for a password */
+	module = gck_slot_get_module (args->slot);
+
+	if (args->auto_login && result == CKR_PIN_INCORRECT) {
+
+		ret = _gck_module_fire_authenticate_slot (module, args->slot, NULL, &args->password);
+
+		/* If authenticate returns TRUE then call is not complete */
+		ret = !ret;
+	}
+
+	g_object_unref (module);
+
+	return ret;
+}
+
+static void
+free_open_session (OpenSession *args)
+{
+	g_assert (!args->password);
+	if (args->slot)
+		g_object_unref (args->slot);
+	g_free (args);
+}
+
+/**
+ * gck_slot_open_session:
+ * @self: The slot ot open a session on.
+ * @flags: The flags to open a session with.
+ * @err: A location to return an error, or NULL.
+ *
+ * Open a session on the slot. If the 'auto reuse' setting is set,
+ * then this may be a recycled session with the same flags.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: A new session or NULL if an error occurs.
+ **/
+GckSession*
+gck_slot_open_session (GckSlot *self, gulong flags, GError **err)
+{
+	return gck_slot_open_session_full (self, flags, NULL, NULL, NULL, err);
+}
+
+/**
+ * gck_slot_open_session_full:
+ * @self: The slot to open a session on.
+ * @flags: The flags to open a session with.
+ * @app_data: Application data for notification callback.
+ * @notify: PKCS#11 notification callback.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @err: A location to return an error, or NULL.
+ *
+ * Open a session on the slot. If the 'auto reuse' setting is set,
+ * then this may be a recycled session with the same flags.
+ *
+ * This call may block for an indefinite period.
+ *
+ * Return value: A new session or NULL if an error occurs.
+ **/
+GckSession*
+gck_slot_open_session_full (GckSlot *self, gulong flags, gpointer app_data,
+                             CK_NOTIFY notify, GCancellable *cancellable, GError **err)
+{
+	GckSession *session = NULL;
+	GckModule *module = NULL;
+	CK_SESSION_HANDLE handle;
+	CK_SLOT_ID slot_id;
+
+	flags |= CKF_SERIAL_SESSION;
+
+	g_object_ref (self);
+
+	/* Try to use a cached session */
+	module = gck_slot_get_module (self);
+	slot_id = gck_slot_get_handle (self);
+	handle = _gck_module_pooled_session_handle (module, slot_id, flags);
+	if (handle != 0)
+		session = make_session_object (self, flags, handle);
+
+	/* Open a new session */
+	if (session == NULL) {
+		OpenSession args = { GCK_ARGUMENTS_INIT, 0,  };
+
+		args.slot = self;
+		args.flags = flags;
+		args.app_data = app_data;
+		args.notify = notify;
+		args.password = NULL;
+		args.auto_login = (gck_module_get_auto_authenticate (module) & GCK_AUTHENTICATE_TOKENS) ? TRUE : FALSE;
+		args.session = 0;
+
+		if (_gck_call_sync (self, perform_open_session, complete_open_session, &args, cancellable, err))
+			session = make_session_object (self, flags, args.session);
+	}
+
+	g_object_unref (module);
+	g_object_unref (self);
+
+	return session;
+}
+
+/**
+ * gck_slot_open_session_async:
+ * @self: The slot to open a session on.
+ * @flags: The flags to open a session with.
+ * @app_data: Application data for notification callback.
+ * @notify: PKCS#11 notification callback.
+ * @cancellable: Optional cancellation object, or NULL.
+ * @callback: Called when the operation completes.
+ * @user_data: Data to pass to the callback.
+ *
+ * Open a session on the slot. If the 'auto reuse' setting is set,
+ * then this may be a recycled session with the same flags.
+ *
+ * This call will return immediately and complete asynchronously.
+ **/
+void
+gck_slot_open_session_async (GckSlot *self, gulong flags, gpointer app_data,
+                              CK_NOTIFY notify, GCancellable *cancellable,
+                              GAsyncReadyCallback callback, gpointer user_data)
+{
+	GckModule *module = NULL;
+	GckCall *call;
+	OpenSession *args;
+	CK_SLOT_ID slot_id;
+
+	flags |= CKF_SERIAL_SESSION;
+
+	g_object_ref (self);
+
+	args =  _gck_call_async_prep (self, self, perform_open_session, complete_open_session,
+	                               sizeof (*args), free_open_session);
+
+	args->flags = flags;
+	args->app_data = app_data;
+	args->notify = notify;
+	args->slot = g_object_ref (self);
+
+	/* Try to use a cached session */
+	module = gck_slot_get_module (self);
+	slot_id = gck_slot_get_handle (self);
+	args->session = _gck_module_pooled_session_handle (module, slot_id, flags);
+	args->auto_login = (gck_module_get_auto_authenticate (module) & GCK_AUTHENTICATE_TOKENS) ? TRUE : FALSE;
+	g_object_unref (module);
+
+	call = _gck_call_async_ready (args, cancellable, callback, user_data);
+	if (args->session)
+		_gck_call_async_short (call, CKR_OK);
+	else
+		_gck_call_async_go (call);
+
+	g_object_unref (self);
+}
+
+/**
+ * gck_slot_open_session_finish:
+ * @self: The slot to open a session on.
+ * @result: The result passed to the callback.
+ * @err: A location to return an error or NULL.
+ *
+ * Get the result of an open session operation. If the 'auto reuse' setting is set,
+ * then this may be a recycled session with the same flags.
+ *
+ * Return value: The new session or NULL if an error occurs.
+ */
+GckSession*
+gck_slot_open_session_finish (GckSlot *self, GAsyncResult *result, GError **err)
+{
+	GckSession *session = NULL;
+
+	g_object_ref (self);
+
+	{
+		OpenSession *args;
+
+		if (_gck_call_basic_finish (result, err)) {
+			args = _gck_call_arguments (result, OpenSession);
+			session = make_session_object (self, args->flags, args->session);
+		}
+	}
+
+	g_object_unref (self);
+
+	return session;
+}
diff --git a/gck/gck.h b/gck/gck.h
new file mode 100644
index 0000000..5e74b95
--- /dev/null
+++ b/gck/gck.h
@@ -0,0 +1,1297 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck.h - the GObject PKCS#11 wrapper library
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+#ifndef GCK_H
+#define GCK_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "pkcs11.h"
+
+G_BEGIN_DECLS
+
+#define             GCK_VENDOR_CODE                         0x47503131 /* GP11 */
+
+/* An error code which results from a failure to load the PKCS11 module */
+#define             CKR_GCK_MODULE_PROBLEM                  (CKR_VENDOR_DEFINED | (GCK_VENDOR_CODE + 1))
+
+#define             GCK_ERROR                               (gck_get_error_quark ())
+
+GQuark              gck_get_error_quark                     (void);
+
+GList*              gck_list_ref_copy                       (GList *reflist);
+
+void                gck_list_unref_free                     (GList *reflist);
+
+const gchar*        gck_message_from_rv                     (CK_RV rv);
+
+gchar*              gck_string_from_chars                   (const guchar *data, gsize max);
+
+typedef gpointer    (*GckAllocator)                        (gpointer data, gsize length);
+
+typedef struct GckMechanism {
+	gulong type;
+	gpointer parameter;
+	gulong n_parameter;
+} GckMechanism;
+
+GckMechanism*       gck_mechanism_new                       (gulong type);
+
+GckMechanism*       gck_mechanism_new_with_param            (gulong type,
+                                                             gconstpointer parameter,
+                                                             gulong n_parameter);
+
+GckMechanism*       gck_mechanism_ref                       (GckMechanism* mech);
+
+void                gck_mechanism_unref                     (GckMechanism* mech);
+
+typedef struct GckAttribute {
+	gulong type;
+	gpointer value;
+	gulong length;
+} GckAttribute;
+
+#define GCK_BOOLEAN  ((gssize)-1)
+#define GCK_ULONG    ((gssize)-2)
+#define GCK_STRING   ((gssize)-3)
+#define GCK_DATE     ((gssize)-4)
+
+#define GCK_INVALID G_MAXULONG
+
+enum {
+	GCK_AUTHENTICATE_TOKENS = 2,
+	GCK_AUTHENTICATE_OBJECTS = 4
+};
+
+void                gck_attribute_init                      (GckAttribute *attr,
+                                                             gulong attr_type,
+                                                             gconstpointer value,
+                                                             gsize length);
+
+void                gck_attribute_init_invalid              (GckAttribute *attr,
+                                                             gulong attr_type);
+
+void                gck_attribute_init_empty                (GckAttribute *attr,
+                                                             gulong attr_type);
+
+void                gck_attribute_init_boolean              (GckAttribute *attr,
+                                                             gulong attr_type,
+                                                             gboolean value);
+
+void                gck_attribute_init_date                 (GckAttribute *attr,
+                                                             gulong attr_type,
+                                                             const GDate *value);
+
+void                gck_attribute_init_ulong                (GckAttribute *attr,
+                                                             gulong attr_type,
+                                                             gulong value);
+
+void                gck_attribute_init_string               (GckAttribute *attr,
+                                                             gulong attr_type,
+                                                             const gchar *value);
+
+void                gck_attribute_init_copy                 (GckAttribute *dest,
+                                                             const GckAttribute *src);
+
+GckAttribute*       gck_attribute_new                       (gulong attr_type,
+                                                             gpointer value,
+                                                             gsize length);
+
+GckAttribute*       gck_attribute_new_invalid               (gulong attr_type);
+
+GckAttribute*       gck_attribute_new_empty                 (gulong attr_type);
+
+GckAttribute*       gck_attribute_new_boolean               (gulong attr_type,
+                                                             gboolean value);
+
+GckAttribute*       gck_attribute_new_date                  (gulong attr_type,
+                                                             const GDate *value);
+
+GckAttribute*       gck_attribute_new_ulong                 (gulong attr_type,
+                                                             gulong value);
+
+GckAttribute*       gck_attribute_new_string                (gulong attr_type,
+                                                             const gchar *value);
+
+gboolean            gck_attribute_is_invalid                (GckAttribute *attr);
+
+gboolean            gck_attribute_get_boolean               (GckAttribute *attr);
+
+gulong              gck_attribute_get_ulong                 (GckAttribute *attr);
+
+gchar*              gck_attribute_get_string                (GckAttribute *attr);
+
+void                gck_attribute_get_date                  (GckAttribute *attr,
+                                                             GDate* value);
+
+GckAttribute*       gck_attribute_dup                       (GckAttribute *attr);
+
+void                gck_attribute_clear                     (GckAttribute *attr);
+
+void                gck_attribute_free                      (GckAttribute *attr);
+
+
+typedef struct _GckAttributes GckAttributes;
+
+#define             GCK_TYPE_ATTRIBUTES                     (gck_attributes_get_boxed_type ())
+
+GType               gck_attributes_get_boxed_type           (void) G_GNUC_CONST;
+
+GckAttributes*      gck_attributes_new                      (void);
+
+GckAttributes*      gck_attributes_new_empty                (gulong attr_type,
+                                                             ...);
+
+GckAttributes*      gck_attributes_new_full                 (GckAllocator allocator);
+
+GckAttributes*      gck_attributes_newv                     (gulong attr_type,
+                                                             ...);
+
+GckAttributes*      gck_attributes_new_valist               (GckAllocator allocator,
+                                                             va_list va);
+
+GckAttribute*       gck_attributes_at                       (GckAttributes *attrs,
+                                                             guint index);
+
+GckAttribute*       gck_attributes_add                      (GckAttributes *attrs,
+                                                             GckAttribute *attr);
+
+GckAttribute*       gck_attributes_add_data                 (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             gconstpointer value,
+                                                             gsize length);
+
+GckAttribute*       gck_attributes_add_invalid              (GckAttributes *attrs,
+                                                             gulong attr_type);
+
+GckAttribute*       gck_attributes_add_empty                (GckAttributes *attrs,
+                                                             gulong attr_type);
+
+GckAttribute*       gck_attributes_add_boolean              (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             gboolean value);
+
+GckAttribute*       gck_attributes_add_string               (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             const gchar *value);
+
+GckAttribute*       gck_attributes_add_date                 (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             const GDate *value);
+
+GckAttribute*       gck_attributes_add_ulong                (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             gulong value);
+
+GckAttribute*       gck_attributes_find                     (GckAttributes *attrs,
+                                                             gulong attr_type);
+
+gboolean            gck_attributes_find_boolean             (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             gboolean *value);
+
+gboolean            gck_attributes_find_ulong               (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             gulong *value);
+
+gboolean            gck_attributes_find_string              (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             gchar **value);
+
+gboolean            gck_attributes_find_date                (GckAttributes *attrs,
+                                                             gulong attr_type,
+                                                             GDate *value);
+
+gulong              gck_attributes_count                    (GckAttributes *attrs);
+
+GckAttributes*      gck_attributes_ref                      (GckAttributes *attrs);
+
+void                gck_attributes_unref                    (GckAttributes *attrs);
+
+/* -------------------------------------------------------------------------
+ * FORWARDS
+ */
+typedef struct _GckSlot GckSlot;
+typedef struct _GckModule GckModule;
+typedef struct _GckSession GckSession;
+typedef struct _GckObject GckObject;
+
+typedef gboolean    (*GckObjectForeachFunc)                (GckObject *object, gpointer user_data);
+
+/* -------------------------------------------------------------------------
+ * MODULE
+ */
+
+typedef struct _GckModuleInfo {
+	guint8 pkcs11_version_major;
+	guint8 pkcs11_version_minor;
+
+	gchar *manufacturer_id;
+	gulong flags;
+
+	gchar *library_description;
+	guint8 library_version_major;
+	guint8 library_version_minor;
+} GckModuleInfo;
+
+void                gck_module_info_free                   (GckModuleInfo *module_info);
+
+#define GCK_TYPE_MODULE             (gck_module_get_type())
+#define GCK_MODULE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_MODULE, GckModule))
+#define GCK_MODULE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_MODULE, GckModule))
+#define GCK_IS_MODULE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_MODULE))
+#define GCK_IS_MODULE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_MODULE))
+#define GCK_MODULE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_MODULE, GckModuleClass))
+
+typedef struct _GckModuleClass GckModuleClass;
+
+struct _GckModule {
+	GObject parent;
+	gpointer reserved[4];
+};
+
+struct _GckModuleClass {
+	GObjectClass parent;
+
+	gboolean (*authenticate_slot) (GckModule *self, GckSlot *slot, gchar *label, gchar **password);
+
+	gboolean (*authenticate_object) (GckModule *self, GckObject *object, gchar *label, gchar **password);
+
+	gpointer reserved[8];
+};
+
+GType                 gck_module_get_type                    (void) G_GNUC_CONST;
+
+GckModule*           gck_module_new                         (CK_FUNCTION_LIST_PTR funcs);
+
+GckModule*           gck_module_initialize                  (const gchar *path,
+                                                               gpointer reserved,
+                                                               GError **err);
+
+gboolean              gck_module_equal                       (gconstpointer module1,
+                                                               gconstpointer module2);
+
+guint                 gck_module_hash                        (gconstpointer module);
+
+const gchar*          gck_module_get_path                    (GckModule *self);
+
+CK_FUNCTION_LIST_PTR  gck_module_get_functions                (GckModule *self);
+
+GckModuleInfo*        gck_module_get_info                     (GckModule *self);
+
+GList*                gck_module_get_slots                    (GckModule *self,
+                                                               gboolean token_present);
+
+gboolean              gck_module_get_pool_sessions            (GckModule *self);
+
+void                  gck_module_set_pool_sessions            (GckModule *self,
+                                                               gboolean pool);
+
+gint                  gck_module_get_auto_authenticate        (GckModule *self);
+
+void                  gck_module_set_auto_authenticate        (GckModule *self,
+                                                               gint auto_authenticate);
+
+gboolean              gck_module_enumerate_objects            (GckModule *self,
+                                                               GckObjectForeachFunc func,
+                                                               gpointer user_data,
+                                                               ...);
+
+gboolean              gck_module_enumerate_objects_full       (GckModule *self,
+                                                               GckAttributes *attrs,
+                                                               GCancellable *cancellable,
+                                                               GckObjectForeachFunc func,
+                                                               gpointer user_data,
+                                                               GError **error);
+
+#ifdef UNIMPLEMENTED
+void                  gck_module_enumerate_objects_async      (GckModule *self,
+                                                               GckAttributes *attrs,
+                                                               GCancellable *cancellable,
+                                                               GAsyncReadyCallback callback,
+                                                               gpointer user_data);
+
+GckObject*            gck_module_enumerate_objects_next       (GckModule *self,
+                                                               GAsyncResult *res,
+                                                               GError **error);
+
+void                  gck_module_enumerate_objects_finish     (GckModule *self,
+                                                               GAsyncResult *res,
+                                                               GError **error);
+#endif
+
+enum {
+	GCK_IS_STRING = -1,
+	GCK_IS_BOOLEAN = -2,
+	GCK_IS_DATE = -3,
+	GCK_IS_ULONG = -4
+};
+
+/* ------------------------------------------------------------------------
+ * SLOT
+ */
+
+typedef struct _GckSlotInfo {
+	gchar *slot_description;
+	gchar *manufacturer_id;
+	gulong flags;
+	guint8 hardware_version_major;
+	guint8 hardware_version_minor;
+	guint8 firmware_version_major;
+	guint8 firmware_version_minor;
+} GckSlotInfo;
+
+void                gck_slot_info_free                      (GckSlotInfo *slot_info);
+
+typedef struct _GckTokenInfo {
+	gchar *label;
+	gchar *manufacturer_id;
+	gchar *model;
+	gchar *serial_number;
+	gulong flags;
+	glong max_session_count;
+	glong session_count;
+	glong max_rw_session_count;
+	glong rw_session_count;
+	glong max_pin_len;
+	glong min_pin_len;
+	glong total_public_memory;
+	glong free_public_memory;
+	glong total_private_memory;
+	glong free_private_memory;
+	guint8 hardware_version_major;
+	guint8 hardware_version_minor;
+	guint8 firmware_version_major;
+	guint8 firmware_version_minor;
+	gint64 utc_time;
+} GckTokenInfo;
+
+void                gck_token_info_free                     (GckTokenInfo *token_info);
+
+typedef struct _GckMechanismInfo {
+	gulong min_key_size;
+	gulong max_key_size;
+	gulong flags;
+} GckMechanismInfo;
+
+void                gck_mechanism_info_free                 (GckMechanismInfo *mech_info);
+
+typedef GArray GckMechanisms;
+
+#define             gck_mechanisms_length(a)                ((a)->len)
+
+#define             gck_mechanisms_at(a, i)                 (g_array_index(a, CK_MECHANISM_TYPE, i))
+
+#define             gck_mechanisms_free(a)                  (g_array_free(a, TRUE))
+
+gboolean            gck_mechanisms_check                    (GckMechanisms *mechanisms,
+                                                             ...);
+
+#define GCK_TYPE_SLOT             (gck_slot_get_type())
+#define GCK_SLOT(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_SLOT, GckSlot))
+#define GCK_SLOT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_SLOT, GckSlot))
+#define GCK_IS_SLOT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_SLOT))
+#define GCK_IS_SLOT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_SLOT))
+#define GCK_SLOT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_SLOT, GckSlotClass))
+
+typedef struct _GckSlotClass GckSlotClass;
+
+struct _GckSlot {
+	GObject parent;
+	gpointer reserved[4];
+};
+
+struct _GckSlotClass {
+	GObjectClass parent;
+	gpointer reserved[9];
+};
+
+GType               gck_slot_get_type                       (void) G_GNUC_CONST;
+
+gboolean            gck_slot_equal                          (gconstpointer slot1,
+                                                             gconstpointer slot2);
+
+guint               gck_slot_hash                           (gconstpointer slot);
+
+GckModule*          gck_slot_get_module                     (GckSlot *self);
+
+CK_SLOT_ID          gck_slot_get_handle                     (GckSlot *self);
+
+GckSlotInfo*        gck_slot_get_info                       (GckSlot *self);
+
+GckTokenInfo*       gck_slot_get_token_info                  (GckSlot *self);
+
+GckMechanisms*      gck_slot_get_mechanisms                 (GckSlot *self);
+
+GckMechanismInfo*   gck_slot_get_mechanism_info             (GckSlot *self,
+                                                             gulong mech_type);
+
+gboolean            gck_slot_has_flags                      (GckSlot *self,
+                                                             gulong flags);
+
+#if UNIMPLEMENTED
+
+gboolean            gck_slot_init_token                     (GckSlot *self,
+                                                             const guchar *pin,
+                                                             gsize length,
+                                                             const gchar *label,
+                                                             GError **err);
+
+
+void                gck_slot_init_token_async               (GckSlot *self,
+                                                             const guchar *pin,
+                                                             gsize length,
+                                                             const gchar *label,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_slot_init_token_finish              (GckSlot *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+GckSession*         gck_slot_open_session                   (GckSlot *self,
+                                                             gulong flags,
+                                                             GError **err);
+
+GckSession*         gck_slot_open_session_full              (GckSlot *self,
+                                                             gulong flags,
+                                                             gpointer app_data,
+                                                             CK_NOTIFY notify,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_slot_open_session_async             (GckSlot *self,
+                                                             gulong flags,
+                                                             gpointer app_data,
+                                                             CK_NOTIFY notify,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GckSession*         gck_slot_open_session_finish            (GckSlot *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+/* ------------------------------------------------------------------------
+ * SESSION
+ */
+
+typedef struct _GckSessionInfo {
+	gulong slot_id;
+	gulong state;
+	gulong flags;
+	gulong device_error;
+} GckSessionInfo;
+
+void                gck_session_info_free                  (GckSessionInfo *session_info);
+
+#define GCK_TYPE_SESSION             (gck_session_get_type())
+#define GCK_SESSION(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_SESSION, GckSession))
+#define GCK_SESSION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_SESSION, GckSession))
+#define GCK_IS_SESSION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_SESSION))
+#define GCK_IS_SESSION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_SESSION))
+#define GCK_SESSION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_SESSION, GckSessionClass))
+
+typedef struct _GckSessionClass GckSessionClass;
+
+struct _GckSession {
+	GObject parent;
+	gpointer reserved[4];
+};
+
+struct _GckSessionClass {
+	GObjectClass parent;
+
+	gboolean (*discard_handle) (GckSession *session, CK_SESSION_HANDLE handle);
+
+	gpointer reserved[8];
+};
+
+GType               gck_session_get_type                    (void) G_GNUC_CONST;
+
+GckSession*         gck_session_from_handle                 (GckSlot *slot,
+                                                             CK_SESSION_HANDLE handle);
+
+GckModule*          gck_session_get_module                  (GckSession *self);
+
+GckSlot*            gck_session_get_slot                    (GckSession *self);
+
+CK_SESSION_HANDLE   gck_session_get_handle                  (GckSession *self);
+
+GckSessionInfo*     gck_session_get_info                    (GckSession *self);
+
+gboolean            gck_session_init_pin                    (GckSession *self,
+                                                             const guchar *pin,
+                                                             gsize n_pin,
+                                                             GError **err);
+
+gboolean            gck_session_init_pin_full               (GckSession *self,
+                                                             const guchar *pin,
+                                                             gsize n_pin,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_init_pin_async              (GckSession *self,
+                                                             const guchar *pin,
+                                                             gsize n_pin,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_init_pin_finish             (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+gboolean            gck_session_set_pin                     (GckSession *self,
+                                                             const guchar *old_pin,
+                                                             gsize n_old_pin,
+                                                             const guchar *new_pin,
+                                                             gsize n_new_pin,
+                                                             GError **err);
+
+gboolean            gck_session_set_pin_full                (GckSession *self,
+                                                             const guchar *old_pin,
+                                                             gsize n_old_pin,
+                                                             const guchar *new_pin,
+                                                             gsize n_new_pin,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_set_pin_async               (GckSession *self,
+                                                             const guchar *old_pin,
+                                                             gsize n_old_pin,
+                                                             const guchar *new_pin,
+                                                             gsize n_new_pin,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_set_pin_finish              (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+gboolean            gck_session_login                       (GckSession *self,
+                                                             gulong user_type,
+                                                             const guchar *pin,
+                                                             gsize n_pin,
+                                                             GError **err);
+
+gboolean            gck_session_login_full                  (GckSession *self,
+                                                             gulong user_type,
+                                                             const guchar *pin,
+                                                             gsize n_pin,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_login_async                 (GckSession *self,
+                                                             gulong user_type,
+                                                             const guchar *pin,
+                                                             gsize n_pin,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_login_finish                (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+gboolean            gck_session_logout                      (GckSession *self,
+                                                             GError **err);
+
+gboolean            gck_session_logout_full                 (GckSession *self,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_logout_async                (GckSession *self,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_logout_finish               (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+GckObject*          gck_session_create_object               (GckSession *self,
+                                                             GError **err,
+                                                             ...);
+
+GckObject*          gck_session_create_object_full          (GckSession *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_create_object_async         (GckSession *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GckObject*          gck_session_create_object_finish        (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+GList*              gck_session_find_objects                (GckSession *self,
+                                                             GError **err,
+                                                             ...);
+
+GList*              gck_session_find_objects_full           (GckSession *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_find_objects_async          (GckSession *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GList*              gck_session_find_objects_finish         (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+#if UNIMPLEMENTED
+
+GckObject*          gck_session_generate_key                (GckSession *self,
+                                                             GckMechanism *mechanism,
+                                                             GError **err,
+                                                             ...);
+
+void                gck_session_generate_key_async          (GckSession *self,
+                                                             GckMechanism *mechanism,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data,
+                                                             ...);
+
+GckObject*          gck_session_generate_key_finish         (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err,
+                                                             ...);
+
+#endif /* UNIMPLEMENTED */
+
+gboolean            gck_session_generate_key_pair_full      (GckSession *self,
+                                                             GckMechanism *mechanism,
+                                                             GckAttributes *public_attrs,
+                                                             GckAttributes *private_attrs,
+                                                             GckObject **public_key,
+                                                             GckObject **private_key,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_session_generate_key_pair_async     (GckSession *self,
+                                                             GckMechanism *mechanism,
+                                                             GckAttributes *public_attrs,
+                                                             GckAttributes *private_attrs,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_generate_key_pair_finish    (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GckObject **public_key,
+                                                             GckObject **private_key,
+                                                             GError **err);
+
+#ifdef UNIMPLEMENTED
+
+gboolean            gck_session_seed_random                 (GckSession *self,
+                                                             const guchar *seed,
+                                                             gsize n_seed,
+                                                             GError **err);
+
+void                gck_session_seed_random_async           (GckSession *self,
+                                                             const guchar *seed,
+                                                             gsize n_seed,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_seed_random_finish          (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+guchar*             gck_session_generate_random             (GckSession *self,
+                                                             gsize n_random,
+                                                             GError **err);
+
+void                gck_session_generate_random_async       (GckSession *self,
+                                                             gsize n_random,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+guchar*             gck_session_generate_random_finish      (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+guchar*             gck_session_encrypt                      (GckSession *self,
+                                                              GckObject *key,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_encrypt_full                 (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_encrypt_async                (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+guchar*             gck_session_encrypt_finish               (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_decrypt                      (GckSession *self,
+                                                              GckObject *key,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_decrypt_full                 (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_decrypt_async                (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+guchar*             gck_session_decrypt_finish               (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+#if UNIMPLEMENTED
+
+guchar*             gck_session_digest                       (GckSession *self,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_digest_full                  (GckSession *self,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_digest_async                 (GckSession *self,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+guchar*             gck_session_digest_finish                (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+guchar*             gck_session_sign                         (GckSession *self,
+                                                              GckObject *key,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_sign_full                    (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_sign_async                   (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+guchar*             gck_session_sign_finish                  (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+#if UNIMPLEMENTED
+
+guchar*             gck_session_sign_recover                 (GckSession *self,
+                                                              GckObject *key,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_sign_recover_full            (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_sign_recover_async           (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+guchar*             gck_session_sign_recover_finish          (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+gboolean            gck_session_verify                       (GckSession *self,
+                                                              GckObject *key,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              const guchar *signature,
+                                                              gsize n_signature,
+                                                              GError **err);
+
+gboolean            gck_session_verify_full                  (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              const guchar *signature,
+                                                              gsize n_signature,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_verify_async                 (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              const guchar *signature,
+                                                              gsize n_signature,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+gboolean            gck_session_verify_finish                (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              GError **err);
+
+#if UNIMPLEMENTED
+
+GkrProcessor*       gck_session_batch_verify                 (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_batch_verify_async           (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+GkrProcessor*       gck_session_batch_verify_finish          (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              GError **err);
+
+guchar*             gck_session_verify_recover               (GckSession *self,
+                                                              GckObject *key,
+                                                              gulong mech_type,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+guchar*             gck_session_verify_recover_full          (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_verify_recover_async         (GckSession *self,
+                                                              GckObject *key,
+                                                              GckMechanism *mechanism,
+                                                              const guchar *input,
+                                                              gsize n_input,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+guchar*             gck_session_verify_recover_finish        (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+gpointer            gck_session_wrap_key                     (GckSession *self,
+                                                              GckObject *wrapper,
+                                                              gulong mech_type,
+                                                              GckObject *wrapped,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+gpointer            gck_session_wrap_key_full                (GckSession *self,
+                                                              GckObject *wrapper,
+                                                              GckMechanism *mechanism,
+                                                              GckObject *wrapped,
+                                                              gsize *n_result,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_wrap_key_async               (GckSession *self,
+                                                              GckObject *wrapper,
+                                                              GckMechanism *mechanism,
+                                                              GckObject *wrapped,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+gpointer            gck_session_wrap_key_finish              (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              gsize *n_result,
+                                                              GError **err);
+
+GckObject*          gck_session_unwrap_key                   (GckSession *self,
+                                                              GckObject *wrapper,
+                                                              gulong mech_type,
+                                                              gconstpointer input,
+                                                              gsize n_input,
+                                                              GError **err,
+                                                              ...);
+
+GckObject*          gck_session_unwrap_key_full              (GckSession *self,
+                                                              GckObject *wrapper,
+                                                              GckMechanism *mechanism,
+                                                              gconstpointer input,
+                                                              gsize n_input,
+                                                              GckAttributes *attrs,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_unwrap_key_async             (GckSession *self,
+                                                              GckObject *wrapper,
+                                                              GckMechanism *mechanism,
+                                                              gconstpointer input,
+                                                              gsize n_input,
+                                                              GckAttributes *attrs,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+GckObject*          gck_session_unwrap_key_finish            (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              GError **err);
+
+GckObject*          gck_session_derive_key                   (GckSession *self,
+                                                              GckObject *base,
+                                                              gulong mech_type,
+                                                              GError **err,
+                                                              ...);
+
+GckObject*          gck_session_derive_key_full              (GckSession *self,
+                                                              GckObject *base,
+                                                              GckMechanism *mechanism,
+                                                              GckAttributes *attrs,
+                                                              GCancellable *cancellable,
+                                                              GError **err);
+
+void                gck_session_derive_key_async             (GckSession *self,
+                                                              GckObject *base,
+                                                              GckMechanism *mechanism,
+                                                              GckAttributes *attrs,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+GckObject*          gck_session_derive_key_finish            (GckSession *self,
+                                                              GAsyncResult *result,
+                                                              GError **err);
+
+/* ------------------------------------------------------------------------
+ * OBJECT
+ */
+
+#define GCK_TYPE_OBJECT             (gck_object_get_type())
+#define GCK_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_OBJECT, GckObject))
+#define GCK_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_OBJECT, GckObject))
+#define GCK_IS_OBJECT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_OBJECT))
+#define GCK_IS_OBJECT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_OBJECT))
+#define GCK_OBJECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_OBJECT, GckObjectClass))
+
+typedef struct _GckObjectClass GckObjectClass;
+
+struct _GckObject {
+	GObject parent;
+	gpointer reserved[4];
+};
+
+struct _GckObjectClass {
+	GObjectClass parent;
+	gpointer reserved[8];
+};
+
+GType               gck_object_get_type                     (void) G_GNUC_CONST;
+
+GckObject*          gck_object_from_handle                  (GckSlot *slot,
+                                                             CK_OBJECT_HANDLE handle);
+
+GList*              gck_objects_from_handle_array           (GckSlot *slot,
+                                                             CK_OBJECT_HANDLE_PTR handles,
+                                                             CK_ULONG n_handles);
+
+gboolean            gck_object_equal                        (gconstpointer object1,
+                                                             gconstpointer object2);
+
+guint               gck_object_hash                         (gconstpointer object);
+
+GckModule*          gck_object_get_module                   (GckObject *self);
+
+GckSlot*            gck_object_get_slot                     (GckObject *self);
+
+CK_OBJECT_HANDLE    gck_object_get_handle                   (GckObject *self);
+
+GckSession*         gck_object_get_session                  (GckObject *self);
+
+void                gck_object_set_session                  (GckObject *self,
+                                                             GckSession *session);
+
+
+#ifdef UNIMPLEMENTED
+
+GckObject*          gck_object_copy                         (GckObject *self,
+                                                             GError **err);
+
+GckObject*          gck_object_copy_full                    (GckObject *self,
+                                                             GckAttributes *additional,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_copy_async                   (GckObject *self,
+                                                             GckAttributes *additional,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GckObject*          gck_object_copy_finish                  (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+gboolean            gck_object_destroy                      (GckObject *self,
+                                                             GError **err);
+
+gboolean            gck_object_destroy_full                 (GckObject *self,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_destroy_async                (GckObject *self,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_object_destroy_finish               (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+#if UNIMPLEMENTED
+
+gssize              gck_object_get_size                     (GckObject *self,
+                                                             GError **err);
+
+gssize              gck_object_get_size_full                (GckObject *self,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_get_size_async               (GckObject *self,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gssize              gck_object_get_size_finish              (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+#endif /* UNIMPLEMENTED */
+
+gboolean            gck_object_set                          (GckObject *self,
+                                                             GError **err,
+                                                             ...);
+
+gboolean            gck_object_set_full                     (GckObject *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_set_async                    (GckObject *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_object_set_finish                   (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+GckAttributes*      gck_object_get                          (GckObject *self,
+                                                             GError **err,
+                                                             ...);
+
+GckAttributes*      gck_object_get_full                     (GckObject *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_get_async                    (GckObject *self,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GckAttributes*      gck_object_get_finish                   (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+gpointer            gck_object_get_data                     (GckObject *self,
+                                                             gulong attr_type,
+                                                             gsize *n_data,
+                                                             GError **err);
+
+gpointer            gck_object_get_data_full                (GckObject *self,
+                                                             gulong attr_type,
+                                                             GckAllocator allocator,
+                                                             GCancellable *cancellable,
+                                                             gsize *n_data,
+                                                             GError **err);
+
+void                gck_object_get_data_async               (GckObject *self,
+                                                             gulong attr_type,
+                                                             GckAllocator allocator,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gpointer            gck_object_get_data_finish              (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             gsize *n_data,
+                                                             GError **err);
+
+gboolean            gck_object_set_template                 (GckObject *self,
+                                                             gulong attr_type,
+                                                             GckAttributes *attrs,
+                                                             GError **err);
+
+gboolean            gck_object_set_template_full            (GckObject *self,
+                                                             gulong attr_type,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_set_template_async           (GckObject *self,
+                                                             gulong attr_type,
+                                                             GckAttributes *attrs,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_object_set_template_finish          (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+GckAttributes*      gck_object_get_template                 (GckObject *self,
+                                                             gulong attr_type,
+                                                             GError **err);
+
+GckAttributes*      gck_object_get_template_full            (GckObject *self,
+                                                             gulong attr_type,
+                                                             GCancellable *cancellable,
+                                                             GError **err);
+
+void                gck_object_get_template_async           (GckObject *self,
+                                                             gulong attr_type,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GckAttributes*      gck_object_get_template_finish          (GckObject *self,
+                                                             GAsyncResult *result,
+                                                             GError **err);
+
+G_END_DECLS
+
+#endif /* GCK_H */
diff --git a/gck/gck.pc.in b/gck/gck.pc.in
new file mode 100644
index 0000000..b0a812d
--- /dev/null
+++ b/gck/gck.pc.in
@@ -0,0 +1,14 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+sysconfdir= sysconfdir@
+
+Name: gp11
+Description: GObject bindings for PKCS#11
+Version: @VERSION@
+Requires: glib-2.0
+Libs: -L${libdir} -lgp11
+Cflags: -I${includedir}/gp11
diff --git a/gck/pkcs11.h b/gck/pkcs11.h
new file mode 100644
index 0000000..c0981c8
--- /dev/null
+++ b/gck/pkcs11.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* pkcs11.h - Dummy pkcs11 file while building
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <nielsen memberwebs com>
+*/
+
+/*
+ * This file is not installed. The one pkcs11/pkcs11.h is installed with the
+ * gp11.h header. However while building we included it here, so that relative
+ * includes work from within gp11.h
+ */
+
+#include "pkcs11/pkcs11.h"
diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am
new file mode 100644
index 0000000..482f6b2
--- /dev/null
+++ b/gck/tests/Makefile.am
@@ -0,0 +1,42 @@
+
+# Keep these in the order they should be tested
+TESTING_FILES = \
+	test-gck-attributes.c \
+	test-gck-mechanism.c \
+	test-gck-module.c \
+	test-gck-slot.c \
+	test-gck-session.c \
+	test-gck-object.c \
+	test-gck-crypto.c
+
+TESTING_FLAGS = \
+	-I$(top_srcdir)/gck/ \
+	-DEXTERNAL_TEST
+
+TESTING_LIBS = \
+	$(GIO_LIBS) \
+	$(top_builddir)/gck/libgck.la \
+	libgck-test-module.la
+
+EXTRA_DIST = \
+	gck-test.h
+
+include $(top_srcdir)/testing/testing.make
+
+# ------------------------------------------------------------------------
+
+lib_LTLIBRARIES = libgck-test-module.la
+
+libgck_test_module_la_LDFLAGS = \
+	-avoid-version
+
+libgck_test_module_la_CFLAGS = \
+	-I$(top_srcdir)/gck \
+	-I$(top_srcdir) \
+	$(GLIB_CFLAGS)
+
+libgck_test_module_la_SOURCES = \
+	gck-test-module.c
+
+libgck_test_module_la_LIBADD = \
+	$(top_builddir)/gck/libgck.la
diff --git a/gck/tests/gck-test-module.c b/gck/tests/gck-test-module.c
new file mode 100644
index 0000000..e6ad4df
--- /dev/null
+++ b/gck/tests/gck-test-module.c
@@ -0,0 +1,1702 @@
+#include "config.h"
+
+#include "gck.h"
+#include "gck-test.h"
+
+#include "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 gboolean logged_in = FALSE;
+static CK_USER_TYPE user_type = 0;
+
+typedef enum _Operation {
+	OP_FIND = 1,
+	OP_CRYPTO
+} Operation;
+
+typedef struct _Session {
+	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;
+
+	/* For 'signing' with CKM_PREFIX */
+	CK_BYTE sign_prefix[128];
+	CK_ULONG n_sign_prefix;
+
+} Session;
+
+static guint unique_identifier = 100;
+static GHashTable *the_sessions = NULL;
+static GHashTable *the_objects = NULL;
+
+enum {
+	PRIVATE_KEY_CAPITALIZE = 3,
+	PUBLIC_KEY_CAPITALIZE = 4,
+	PRIVATE_KEY_PREFIX = 5,
+	PUBLIC_KEY_PREFIX = 6
+};
+
+#define SIGNED_PREFIX "signed-prefix:"
+
+/*
+ * This is not a generic test module, it works in concert with the
+ * test-gck-module.c
+ */
+
+static void
+free_session (gpointer data)
+{
+	Session *sess = (Session*)data;
+	if (sess)
+		g_hash_table_destroy (sess->objects);
+	g_free (sess);
+}
+
+static GckAttributes*
+lookup_object (Session *session, CK_OBJECT_HANDLE hObject)
+{
+	GckAttributes *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;
+}
+
+static CK_RV
+test_C_Initialize (CK_VOID_PTR pInitArgs)
+{
+	GckAttributes *attrs;
+	CK_C_INITIALIZE_ARGS_PTR args;
+	CK_ULONG value;
+	void *mutex;
+	CK_RV rv;
+
+	g_assert (initialized == FALSE && "Initialized same module twice, maybe module was not finalized, outstanding refs?");
+	g_assert (pInitArgs != NULL && "Missing arguments");
+
+	args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs;
+	g_assert (args->CreateMutex != NULL && "Missing CreateMutex");
+	g_assert (args->DestroyMutex != NULL && "Missing DestroyMutex");
+	g_assert (args->LockMutex != NULL && "Missing LockMutex");
+	g_assert (args->UnlockMutex != NULL && "Missing UnlockMutex");
+
+	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");
+
+	/* Flags should allow OS locking and os threads */
+	g_assert ((args->flags & CKF_OS_LOCKING_OK) == CKF_OS_LOCKING_OK && "Invalid CKF_OS_LOCKING_OK flag");
+	g_assert ((args->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) == 0 && "Invalid CKF_LIBRARY_CANT_CREATE_OS_THREADS flag");
+
+	the_pin = g_strdup ("booo");
+	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)gck_attributes_unref);
+
+	/* Our token object */
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                              CKA_LABEL, GCK_STRING, "TEST LABEL",
+	                              GCK_INVALID);
+	g_hash_table_insert (the_objects, GUINT_TO_POINTER (2), attrs);
+
+	/* Private capitalize key */
+	value = CKM_CAPITALIZE;
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PRIVATE_KEY,
+	                              CKA_LABEL, GCK_STRING, "Private Capitalize Key",
+	                              CKA_ALLOWED_MECHANISMS, sizeof (value), &value,
+	                              CKA_DECRYPT, GCK_BOOLEAN, TRUE,
+	                              CKA_PRIVATE, GCK_BOOLEAN, TRUE,
+	                              CKA_WRAP, GCK_BOOLEAN, TRUE,
+	                              CKA_UNWRAP, GCK_BOOLEAN, TRUE,
+	                              CKA_DERIVE, GCK_BOOLEAN, TRUE,
+	                              CKA_VALUE, GCK_STRING, "value",
+	                              GCK_INVALID);
+	g_hash_table_insert (the_objects, GUINT_TO_POINTER (PRIVATE_KEY_CAPITALIZE), attrs);
+
+	/* Public capitalize key */
+	value = CKM_CAPITALIZE;
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PUBLIC_KEY,
+	                              CKA_LABEL, GCK_STRING, "Public Capitalize Key",
+	                              CKA_ALLOWED_MECHANISMS, sizeof (value), &value,
+	                              CKA_ENCRYPT, GCK_BOOLEAN, TRUE,
+	                              CKA_PRIVATE, GCK_BOOLEAN, FALSE,
+	                              CKA_VALUE, GCK_STRING, "value",
+	                              GCK_INVALID);
+	g_hash_table_insert (the_objects, GUINT_TO_POINTER (PUBLIC_KEY_CAPITALIZE), attrs);
+
+	/* Private prefix key */
+	value = CKM_PREFIX;
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PRIVATE_KEY,
+	                              CKA_LABEL, GCK_STRING, "Private prefix key",
+	                              CKA_ALLOWED_MECHANISMS, sizeof (value), &value,
+	                              CKA_SIGN, GCK_BOOLEAN, TRUE,
+	                              CKA_PRIVATE, GCK_BOOLEAN, TRUE,
+	                              CKA_ALWAYS_AUTHENTICATE, GCK_BOOLEAN, TRUE,
+	                              CKA_VALUE, GCK_STRING, "value",
+	                              GCK_INVALID);
+	g_hash_table_insert (the_objects, GUINT_TO_POINTER (PRIVATE_KEY_PREFIX), attrs);
+
+	/* Private prefix key */
+	value = CKM_PREFIX;
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_PUBLIC_KEY,
+	                              CKA_LABEL, GCK_STRING, "Public prefix key",
+	                              CKA_ALLOWED_MECHANISMS, sizeof (value), &value,
+	                              CKA_VERIFY, GCK_BOOLEAN, TRUE,
+	                              CKA_PRIVATE, GCK_BOOLEAN, FALSE,
+	                              CKA_VALUE, GCK_STRING, "value",
+	                              GCK_INVALID);
+	g_hash_table_insert (the_objects, GUINT_TO_POINTER (PUBLIC_KEY_PREFIX), attrs);
+
+	initialized = TRUE;
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_Finalize (CK_VOID_PTR pReserved)
+{
+	g_assert (pReserved == NULL && "Invalid reserved pointer");
+	g_assert (initialized == TRUE && "Finalize without being initialized");
+
+	initialized = 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 }
+};
+
+static CK_RV
+test_C_GetInfo (CK_INFO_PTR pInfo)
+{
+	g_assert (pInfo != NULL && "Invalid pointer to GetInfo");
+	memcpy (pInfo, &TEST_INFO, sizeof (*pInfo));
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+	g_assert (list != NULL && "Invalid pointer passed to GetFunctionList");
+	return C_GetFunctionList (list);
+}
+
+#define TEST_SLOT_ONE  52
+#define TEST_SLOT_TWO  134
+
+/*
+ * Two slots
+ *  ONE: token present
+ *  TWO: token not present
+ */
+
+static CK_RV
+test_C_GetSlotList (CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount)
+{
+	CK_ULONG count;
+
+	g_assert (pulCount != NULL && "Invalid pulCount");
+
+	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_assert (*pulCount && "Passed in a bad count");
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	*pulCount = count;
+	pSlotList[0] = TEST_SLOT_ONE;
+	if (!tokenPresent)
+		pSlotList[1] = TEST_SLOT_TWO;
+
+	return CKR_OK;
+}
+
+static const CK_SLOT_INFO TEST_INFO_ONE = {
+	"TEST SLOT                                                       ",
+	"TEST MANUFACTURER              ",
+	CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE,
+	{ 55, 155 },
+	{ 65, 165 },
+};
+
+static const CK_SLOT_INFO TEST_INFO_TWO = {
+	"TEST SLOT                                                       ",
+	"TEST MANUFACTURER              ",
+	CKF_REMOVABLE_DEVICE,
+	{ 55, 155 },
+	{ 65, 165 },
+};
+
+static CK_RV
+test_C_GetSlotInfo (CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
+{
+	g_assert (pInfo != NULL && "Invalid pInfo");
+
+	if (slotID == TEST_SLOT_ONE) {
+		memcpy (pInfo, &TEST_INFO_ONE, sizeof (*pInfo));
+		return CKR_OK;
+	} else if (slotID == TEST_SLOT_TWO) {
+		memcpy (pInfo, &TEST_INFO_TWO, sizeof (*pInfo));
+		return CKR_OK;
+	} else {
+		g_assert_not_reached (); /* "Invalid slot id" */
+		return CKR_SLOT_ID_INVALID;
+	}
+}
+
+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' }
+};
+
+static CK_RV
+test_C_GetTokenInfo (CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
+{
+	g_assert (pInfo != NULL && "Invalid pInfo");
+
+	if (slotID == TEST_SLOT_ONE) {
+		memcpy (pInfo, &TEST_TOKEN_ONE, sizeof (*pInfo));
+		return CKR_OK;
+	} else if (slotID == TEST_SLOT_TWO) {
+		return CKR_TOKEN_NOT_PRESENT;
+	} else {
+		g_assert_not_reached (); /* "Invalid slot id" */
+		return CKR_SLOT_ID_INVALID;
+	}
+}
+
+/*
+ * TWO mechanisms:
+ *  CKM_CAPITALIZE
+ *  CKM_PREFIX
+ */
+
+static CK_RV
+test_C_GetMechanismList (CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList,
+                         CK_ULONG_PTR pulCount)
+{
+	g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID");
+	g_assert (pulCount != NULL && "Invalid pulCount");
+
+	/* Application only wants to know the number of slots. */
+	if (pMechanismList == NULL) {
+		*pulCount = 2;
+		return CKR_OK;
+	}
+
+	if (*pulCount != 2) {
+		g_assert (*pulCount && "Passed in a bad count");
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	pMechanismList[0] = CKM_CAPITALIZE;
+	pMechanismList[1] = CKM_PREFIX;
+	return CKR_OK;
+}
+
+static const CK_MECHANISM_INFO TEST_MECH_CAPITALIZE = {
+	512, 4096, 0
+};
+
+static const CK_MECHANISM_INFO TEST_MECH_PREFIX = {
+	2048, 2048, 0
+};
+
+static CK_RV
+test_C_GetMechanismInfo (CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
+                         CK_MECHANISM_INFO_PTR pInfo)
+{
+	g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID");
+	g_assert (pInfo != NULL && "Invalid pInfo");
+
+	if (type == CKM_CAPITALIZE) {
+		memcpy (pInfo, &TEST_MECH_CAPITALIZE, sizeof (*pInfo));
+		return CKR_OK;
+	} else if (type == CKM_PREFIX) {
+		memcpy (pInfo, &TEST_MECH_PREFIX, sizeof (*pInfo));
+		return CKR_OK;
+	} else {
+		g_assert_not_reached (); /* "Invalid type" */
+		return CKR_MECHANISM_INVALID;
+	}
+}
+
+static CK_RV
+test_C_InitToken (CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen,
+                  CK_UTF8CHAR_PTR pLabel)
+{
+	g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID");
+	g_assert (pPin != NULL && "Invalid pPin");
+	g_assert (strlen ("TEST PIN") && "Invalid ulPinLen");
+	g_assert (strncmp ((gchar*)pPin, "TEST PIN", ulPinLen) == 0 && "Invalid pPin string");
+	g_assert (pLabel != NULL && "Invalid pLabel");
+	g_assert (strcmp ((gchar*)pPin, "TEST LABEL") == 0 && "Invalid pLabel string");
+
+	g_free (the_pin);
+	the_pin = g_strndup ((gchar*)pPin, ulPinLen);
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_WaitForSlotEvent (CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_OpenSession (CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication,
+                    CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession)
+{
+	Session *sess;
+
+	g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID");
+	g_assert (pApplication == NULL && "pApplication should be null");
+	g_assert (Notify == NULL && "Notify should be null");
+	g_assert (phSession != NULL && "Invalid phSession");
+	g_assert ((flags & CKF_SERIAL_SESSION) == CKF_SERIAL_SESSION);
+
+	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)gck_attributes_unref);
+	*phSession = sess->handle;
+
+	g_hash_table_replace (the_sessions, GUINT_TO_POINTER (sess->handle), sess);
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_CloseSession (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_hash_table_remove (the_sessions, GUINT_TO_POINTER (hSession));
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_CloseAllSessions (CK_SLOT_ID slotID)
+{
+	g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID");
+
+	g_hash_table_remove_all (the_sessions);
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_GetFunctionStatus (CK_SESSION_HANDLE hSession)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+static CK_RV
+test_C_CancelFunction (CK_SESSION_HANDLE hSession)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+static CK_RV
+test_C_GetSessionInfo (CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
+{
+	Session *session;
+
+	g_assert (pInfo != NULL && "Invalid pInfo");
+
+	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;
+
+	memcpy (pInfo, &session->info, sizeof (*pInfo));
+	return CKR_OK;
+}
+
+static CK_RV
+test_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_assert (session != NULL && "No such session found");
+	if (!session)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	g_free (the_pin);
+	the_pin = g_strndup ((gchar*)pPin, ulPinLen);
+	return CKR_OK;
+}
+
+static CK_RV
+test_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_assert (session != NULL && "No such session found");
+	if (!session)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	old = g_strndup ((gchar*)pOldPin, ulOldLen);
+	if (!g_str_equal (old, the_pin))
+		return CKR_PIN_INCORRECT;
+
+	g_free (the_pin);
+	the_pin = g_strndup ((gchar*)pNewPin, ulNewLen);
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_GetOperationState (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
+                        CK_ULONG_PTR pulOperationStateLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_SetOperationState (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState,
+                        CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
+                        CK_OBJECT_HANDLE hAuthenticationKey)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_Login (CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
+              CK_UTF8CHAR_PTR pPin, CK_ULONG pPinLen)
+{
+	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;
+
+	if (!pPin)
+		return CKR_PIN_INCORRECT;
+
+	g_assert (pPinLen == strlen (the_pin) && "Wrong PIN length");
+	g_assert (strncmp ((gchar*)pPin, the_pin, pPinLen) == 0 && "Wrong PIN");
+	g_assert ((userType == CKU_SO || userType == CKU_USER || userType == CKU_CONTEXT_SPECIFIC) && "Bad user type");
+	g_assert (logged_in == FALSE && "Already logged in");
+
+	if (userType == CKU_CONTEXT_SPECIFIC) {
+		session->want_context_login = CK_FALSE;
+	} else {
+		logged_in = TRUE;
+		user_type = userType;
+	}
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_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;
+}
+
+static CK_RV
+test_C_CreateObject (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
+                     CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject)
+{
+	GckAttributes *attrs;
+	Session *session;
+	gboolean token, priv;
+	CK_ULONG i;
+
+	g_assert (phObject != NULL);
+
+	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;
+
+	attrs = gck_attributes_new ();
+	for (i = 0; i < ulCount; ++i)
+		gck_attributes_add_data (attrs, pTemplate[i].type, pTemplate[i].pValue, pTemplate[i].ulValueLen);
+
+	if (gck_attributes_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) {
+		if (!logged_in) {
+			gck_attributes_unref (attrs);
+			return CKR_USER_NOT_LOGGED_IN;
+		}
+	}
+
+	*phObject = ++unique_identifier;
+	if (gck_attributes_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;
+}
+
+static CK_RV
+test_C_CopyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                 CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+                 CK_OBJECT_HANDLE_PTR phNewObject)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+
+static CK_RV
+test_C_DestroyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+	GckAttributes *attrs;
+	Session *session;
+	gboolean priv;
+
+	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;
+
+	attrs = lookup_object (session, hObject);
+	if (!attrs) {
+		g_assert_not_reached (); /* "no such object found" */
+		return CKR_OBJECT_HANDLE_INVALID;
+	}
+
+	if (gck_attributes_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) {
+		if (!logged_in)
+			return CKR_USER_NOT_LOGGED_IN;
+	}
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_GetObjectSize (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                    CK_ULONG_PTR pulSize)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_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;
+	GckAttributes *attrs;
+	GckAttribute *attr;
+	Session *session;
+	CK_ULONG i;
+
+	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;
+
+	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 = gck_attributes_find (attrs, result->type);
+		if (!attr) {
+			result->ulValueLen = (CK_ULONG)-1;
+			ret = CKR_ATTRIBUTE_TYPE_INVALID;
+			continue;
+		}
+
+		if (!result->pValue) {
+			result->ulValueLen = attr->length;
+			continue;
+		}
+
+		if (result->ulValueLen >= attr->length) {
+			memcpy (result->pValue, attr->value, attr->length);
+			continue;
+		}
+
+		result->ulValueLen = (CK_ULONG)-1;
+		ret = CKR_BUFFER_TOO_SMALL;
+	}
+
+	return ret;
+}
+
+static CK_RV
+test_C_SetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+                        CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+	Session *session;
+	CK_ATTRIBUTE_PTR set;
+	GckAttributes *attrs;
+	GckAttribute *attr;
+	CK_ULONG i;
+
+	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;
+
+	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) {
+		set = pTemplate + i;
+		attr = gck_attributes_find (attrs, set->type);
+		if (!attr) {
+			gck_attributes_add_data (attrs, set->type, set->pValue, set->ulValueLen);
+		} else {
+			gck_attribute_clear (attr);
+			gck_attribute_init (attr, set->type, set->pValue, set->ulValueLen);
+		}
+	}
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_FindObjectsInit (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
+                        CK_ULONG ulCount)
+{
+	GHashTableIter iter;
+	GckAttributes *attrs;
+	GckAttribute *attr;
+	CK_ATTRIBUTE_PTR match;
+	Session *session;
+	gpointer key, value;
+	gboolean matched = TRUE;
+	CK_ULONG i;
+
+	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;
+
+	/* Starting an operation, cancels any previous one */
+	if (session->operation != 0)
+		session->operation = 0;
+
+	session->operation = OP_FIND;
+
+	/* Token objects */
+	g_hash_table_iter_init (&iter, the_objects);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		attrs = (GckAttributes*)value;
+		matched = TRUE;
+		for (i = 0; i < ulCount; ++i) {
+			match = pTemplate + i;
+			attr = gck_attributes_find (attrs, match->type);
+			if (!attr) {
+				matched = FALSE;
+				break;
+			}
+
+			if (attr->length != match->ulValueLen ||
+			    memcmp (attr->value, match->pValue, attr->length) != 0) {
+				matched = FALSE;
+				break;
+			}
+		}
+
+		if (matched)
+			session->matches = g_list_prepend (session->matches, key);
+	}
+
+	/* session objects */
+	g_hash_table_iter_init (&iter, session->objects);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		attrs = (GckAttributes*)value;
+		matched = TRUE;
+		for (i = 0; i < ulCount; ++i) {
+			match = pTemplate + i;
+			attr = gck_attributes_find (attrs, match->type);
+			if (!attr) {
+				matched = FALSE;
+				break;
+			}
+
+			if (attr->length != match->ulValueLen ||
+			    memcmp (attr->value, match->pValue, attr->length) != 0) {
+				matched = FALSE;
+				break;
+			}
+		}
+
+		if (matched)
+			session->matches = g_list_prepend (session->matches, key);
+	}
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_FindObjects (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject,
+                  CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount)
+{
+	Session *session;
+
+	g_assert (phObject != NULL);
+	g_assert (pulObjectCount != NULL);
+	g_assert (ulMaxObjectCount != 0);
+
+	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 (session->operation != OP_FIND) {
+		g_assert_not_reached (); /* "invalid call to FindObjects" */
+		return 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;
+}
+
+static CK_RV
+test_C_FindObjectsFinal (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;
+
+	if (session->operation != OP_FIND) {
+		g_assert_not_reached (); /* "invalid call to FindObjectsFinal" */
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	session->operation = 0;
+	g_list_free (session->matches);
+	session->matches = NULL;
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_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_assert (session != NULL && "No such session found");
+	if (!session)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	/* Starting an operation, cancels any previous one */
+	if (session->operation != 0)
+		session->operation = 0;
+
+	g_assert (pMechanism);
+	g_assert (pMechanism->mechanism == CKM_CAPITALIZE);
+	g_assert (hKey == PUBLIC_KEY_CAPITALIZE);
+
+	session->operation = OP_CRYPTO;
+	session->crypto_method = CKA_ENCRYPT;
+	session->crypto_mechanism = CKM_CAPITALIZE;
+	session->crypto_key = hKey;
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
+                CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	Session *session;
+	CK_ULONG i;
+
+	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 (session->operation != OP_CRYPTO) {
+		g_assert_not_reached (); /* "invalid call to Encrypt" */
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	g_assert (pData);
+	g_assert (pulEncryptedDataLen);
+	g_assert (session->crypto_method == CKA_ENCRYPT);
+	g_assert (session->crypto_mechanism == CKM_CAPITALIZE);
+	g_assert (session->crypto_key == PUBLIC_KEY_CAPITALIZE);
+
+	if (!pEncryptedData) {
+		*pulEncryptedDataLen = ulDataLen;
+		return CKR_OK;
+	}
+
+	if (*pulEncryptedDataLen < ulDataLen) {
+		*pulEncryptedDataLen = ulDataLen;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	for (i = 0; i < ulDataLen; ++i)
+		pEncryptedData[i] = g_ascii_toupper (pData[i]);
+	*pulEncryptedDataLen = ulDataLen;
+
+	session->operation = 0;
+	session->crypto_method = 0;
+	session->crypto_mechanism = 0;
+	session->crypto_key = 0;
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_EncryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+                    CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+                    CK_ULONG_PTR pulEncryptedPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_EncryptFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart,
+                   CK_ULONG_PTR pulLastEncryptedPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_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_assert (session != NULL && "No such session found");
+	if (!session)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	/* Starting an operation, cancels any previous one */
+	if (session->operation != 0)
+		session->operation = 0;
+
+	g_assert (pMechanism);
+	g_assert (pMechanism->mechanism == CKM_CAPITALIZE);
+	g_assert (hKey == PRIVATE_KEY_CAPITALIZE);
+
+	session->operation = OP_CRYPTO;
+	session->crypto_method = CKA_DECRYPT;
+	session->crypto_mechanism = CKM_CAPITALIZE;
+	session->crypto_key = hKey;
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_Decrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData,
+                CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+{
+	Session *session;
+	CK_ULONG i;
+
+	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 (session->operation != OP_CRYPTO) {
+		g_assert_not_reached (); /* "invalid call to Encrypt" */
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	g_assert (pEncryptedData);
+	g_assert (pulDataLen);
+	g_assert (session->crypto_method == CKA_DECRYPT);
+	g_assert (session->crypto_mechanism == CKM_CAPITALIZE);
+	g_assert (session->crypto_key == PRIVATE_KEY_CAPITALIZE);
+
+	if (!pData) {
+		*pulDataLen = ulEncryptedDataLen;
+		return CKR_OK;
+	}
+
+	if (*pulDataLen < ulEncryptedDataLen) {
+		*pulDataLen = ulEncryptedDataLen;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	for (i = 0; i < ulEncryptedDataLen; ++i)
+		pData[i] = g_ascii_tolower (pEncryptedData[i]);
+	*pulDataLen = ulEncryptedDataLen;
+
+	session->operation = 0;
+	session->crypto_method = 0;
+	session->crypto_mechanism = 0;
+	session->crypto_key = 0;
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_DecryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart,
+                    CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DecryptFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart,
+                   CK_ULONG_PTR pulLastPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DigestInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_Digest (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
+             CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DigestUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DigestKey (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DigestFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest,
+                  CK_ULONG_PTR pulDigestLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_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_assert (session != NULL && "No such session found");
+	if (!session)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	/* Starting an operation, cancels any previous one */
+	if (session->operation != 0)
+		session->operation = 0;
+
+	g_assert (pMechanism);
+	g_assert (pMechanism->mechanism == CKM_PREFIX);
+	g_assert (hKey == PRIVATE_KEY_PREFIX);
+
+	session->operation = OP_CRYPTO;
+	session->crypto_method = CKA_SIGN;
+	session->crypto_mechanism = CKM_PREFIX;
+	session->crypto_key = hKey;
+
+	if (pMechanism->pParameter) {
+		g_assert (pMechanism->ulParameterLen < sizeof (session->sign_prefix));
+		memcpy (session->sign_prefix, pMechanism->pParameter, pMechanism->ulParameterLen);
+		session->n_sign_prefix = pMechanism->ulParameterLen;
+	} else {
+		g_assert (strlen (SIGNED_PREFIX) + 1 < sizeof (session->sign_prefix));
+		strcpy ((gchar*)session->sign_prefix, SIGNED_PREFIX);
+		session->n_sign_prefix = strlen (SIGNED_PREFIX);
+	}
+
+	/* The private key has CKA_ALWAYS_AUTHENTICATE above */
+	session->want_context_login = CK_TRUE;
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_Sign (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
+            CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	Session *session;
+	CK_ULONG length;
+
+	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 (session->operation != OP_CRYPTO) {
+		g_assert_not_reached (); /* "invalid call to Encrypt" */
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	if (session->want_context_login)
+		return CKR_USER_NOT_LOGGED_IN;
+
+	g_assert (pData);
+	g_assert (pulSignatureLen);
+	g_assert (session->crypto_method == CKA_SIGN);
+	g_assert (session->crypto_mechanism == CKM_PREFIX);
+	g_assert (session->crypto_key == PRIVATE_KEY_PREFIX);
+
+	length = session->n_sign_prefix + ulDataLen;
+
+	if (!pSignature) {
+		*pulSignatureLen = length;
+		return CKR_OK;
+	}
+
+	if (*pulSignatureLen < length) {
+		*pulSignatureLen = length;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	memcpy (pSignature, session->sign_prefix, session->n_sign_prefix);
+	memcpy (pSignature + session->n_sign_prefix, pData, ulDataLen);
+	*pulSignatureLen = length;
+
+	session->operation = 0;
+	session->crypto_method = 0;
+	session->crypto_mechanism = 0;
+	session->crypto_key = 0;
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_SignUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_SignFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+                CK_ULONG_PTR pulSignatureLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_SignRecoverInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+                      CK_OBJECT_HANDLE hKey)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_SignRecover (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
+                  CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_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_assert (session != NULL && "No such session found");
+	if (!session)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	/* Starting an operation, cancels any previous one */
+	if (session->operation != 0)
+		session->operation = 0;
+
+	g_assert (pMechanism);
+	g_assert (pMechanism->mechanism == CKM_PREFIX);
+	g_assert (hKey == PUBLIC_KEY_PREFIX);
+
+	session->operation = OP_CRYPTO;
+	session->crypto_method = CKA_VERIFY;
+	session->crypto_mechanism = CKM_PREFIX;
+	session->crypto_key = hKey;
+
+	if (pMechanism->pParameter) {
+		g_assert (pMechanism->ulParameterLen < sizeof (session->sign_prefix));
+		memcpy (session->sign_prefix, pMechanism->pParameter, pMechanism->ulParameterLen);
+		session->n_sign_prefix = pMechanism->ulParameterLen;
+	} else {
+		g_assert (strlen (SIGNED_PREFIX) + 1 < sizeof (session->sign_prefix));
+		strcpy ((gchar*)session->sign_prefix, SIGNED_PREFIX);
+		session->n_sign_prefix = strlen (SIGNED_PREFIX);
+	}
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_Verify (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
+               CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	Session *session;
+	CK_ULONG length;
+
+	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 (session->operation != OP_CRYPTO) {
+		g_assert_not_reached (); /* "invalid call to Encrypt" */
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	g_assert (pData);
+	g_assert (pSignature);
+	g_assert (session->crypto_method == CKA_VERIFY);
+	g_assert (session->crypto_mechanism == CKM_PREFIX);
+	g_assert (session->crypto_key == PUBLIC_KEY_PREFIX);
+
+	length = session->n_sign_prefix + ulDataLen;
+
+	if (ulSignatureLen < length) {
+		g_assert (FALSE);
+		return CKR_SIGNATURE_LEN_RANGE;
+	}
+
+	if (memcmp (pSignature, session->sign_prefix, session->n_sign_prefix) == 0 &&
+	    memcmp (pSignature + session->n_sign_prefix, pData, ulDataLen) == 0)
+		return CKR_OK;
+
+	return CKR_SIGNATURE_INVALID;
+}
+
+static CK_RV
+test_C_VerifyUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_VerifyFinal (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+                  CK_ULONG pulSignatureLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_VerifyRecoverInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+                        CK_OBJECT_HANDLE hKey)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_VerifyRecover (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+                    CK_ULONG pulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DigestEncryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+                          CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+                          CK_ULONG_PTR ulEncryptedPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DecryptDigestUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart,
+                          CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart,
+                          CK_ULONG_PTR pulPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_SignEncryptUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+                        CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+                        CK_ULONG_PTR ulEncryptedPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_DecryptVerifyUpdate (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart,
+                          CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart,
+                          CK_ULONG_PTR pulPartLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_GenerateKey (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+                  CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+                  CK_OBJECT_HANDLE_PTR phKey)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_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)
+{
+	GckAttributes *attrs;
+	Session *session;
+	gboolean token;
+	CK_ULONG i;
+
+	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 (pMechanism);
+	g_assert (pPublicKeyTemplate);
+	g_assert (ulPublicKeyAttributeCount);
+	g_assert (pPrivateKeyTemplate);
+	g_assert (phPublicKey);
+	g_assert (phPrivateKey);
+
+	if (pMechanism->mechanism != CKM_GENERATE)
+		return CKR_MECHANISM_INVALID;
+
+	if (!pMechanism->pParameter || pMechanism->ulParameterLen != 9 ||
+	    memcmp (pMechanism->pParameter, "generate", 9) != 0) {
+		g_assert_not_reached ();
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+
+	attrs = gck_attributes_new ();
+	gck_attributes_add_string (attrs, CKA_VALUE, "generated");
+	for (i = 0; i < ulPublicKeyAttributeCount; ++i)
+		gck_attributes_add_data (attrs, pPublicKeyTemplate[i].type,
+		                          pPublicKeyTemplate[i].pValue,
+		                          pPublicKeyTemplate[i].ulValueLen);
+	*phPublicKey = ++unique_identifier;
+	if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token)
+		g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phPublicKey), attrs);
+	else
+		g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phPublicKey), attrs);
+
+	attrs = gck_attributes_new ();
+	gck_attributes_add_string (attrs, CKA_VALUE, "generated");
+	for (i = 0; i < ulPrivateKeyAttributeCount; ++i)
+		gck_attributes_add_data (attrs, pPrivateKeyTemplate[i].type,
+		                          pPrivateKeyTemplate[i].pValue,
+		                          pPrivateKeyTemplate[i].ulValueLen);
+	*phPrivateKey = ++unique_identifier;
+	if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token)
+		g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phPrivateKey), attrs);
+	else
+		g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phPrivateKey), attrs);
+	return CKR_OK;
+}
+
+static CK_RV
+test_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)
+{
+	GckAttributes *attrs;
+	GckAttribute *attr;
+	Session *session;
+
+	session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+	if (!session) {
+		g_assert_not_reached ();
+		return CKR_SESSION_HANDLE_INVALID;
+	}
+
+	g_assert (pMechanism);
+	g_assert (hWrappingKey);
+	g_assert (hKey);
+	g_assert (pulWrappedKeyLen);
+
+	attrs = lookup_object (session, hWrappingKey);
+	if (!attrs) {
+		g_assert_not_reached ();
+		return CKR_WRAPPING_KEY_HANDLE_INVALID;
+	}
+
+	attrs = lookup_object (session, hKey);
+	if (!attrs) {
+		g_assert_not_reached ();
+		return CKR_WRAPPED_KEY_INVALID;
+	}
+
+	if (pMechanism->mechanism != CKM_WRAP)
+		return CKR_MECHANISM_INVALID;
+
+	if (pMechanism->pParameter) {
+		if (pMechanism->ulParameterLen != 4 ||
+		    memcmp (pMechanism->pParameter, "wrap", 4) != 0) {
+			g_assert_not_reached ();
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+	}
+
+	attr = gck_attributes_find (attrs, CKA_VALUE);
+	if (attr == NULL)
+		return CKR_WRAPPED_KEY_INVALID;
+
+	if (!pWrappedKey) {
+		*pulWrappedKeyLen = attr->length;
+		return CKR_OK;
+	}
+
+	if (*pulWrappedKeyLen < attr->length) {
+		*pulWrappedKeyLen = attr->length;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	memcpy (pWrappedKey, attr->value, attr->length);
+	*pulWrappedKeyLen = attr->length;
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_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)
+{
+	GckAttributes *attrs;
+	Session *session;
+	gboolean token;
+	CK_ULONG i;
+
+	session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+	if (!session) {
+		g_assert_not_reached ();
+		return CKR_SESSION_HANDLE_INVALID;
+	}
+
+	g_assert (pMechanism);
+	g_assert (pUnwrappingKey);
+	g_assert (pWrappedKey);
+	g_assert (pulWrappedKeyLen);
+	g_assert (phKey);
+	g_assert (ulCount);
+	g_assert (pTemplate);
+	g_assert (phKey);
+
+	attrs = lookup_object (session, pUnwrappingKey);
+	if (!attrs) {
+		g_assert_not_reached ();
+		return CKR_WRAPPING_KEY_HANDLE_INVALID;
+	}
+
+	if (pMechanism->mechanism != CKM_WRAP)
+		return CKR_MECHANISM_INVALID;
+
+	if (pMechanism->pParameter) {
+		if (pMechanism->ulParameterLen != 4 ||
+		    memcmp (pMechanism->pParameter, "wrap", 4) != 0) {
+			g_assert_not_reached ();
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+	}
+
+	attrs = gck_attributes_new ();
+	gck_attributes_add_data (attrs, CKA_VALUE, pWrappedKey, pulWrappedKeyLen);
+	for (i = 0; i < ulCount; ++i)
+		gck_attributes_add_data (attrs, pTemplate[i].type,
+		                          pTemplate[i].pValue,
+		                          pTemplate[i].ulValueLen);
+	*phKey = ++unique_identifier;
+	if (gck_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token)
+		g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phKey), attrs);
+	else
+		g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phKey), attrs);
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_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)
+{
+	GckAttributes *attrs, *copy;
+	Session *session;
+	gboolean token;
+	CK_ULONG i;
+
+	session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+	if (!session) {
+		g_assert_not_reached ();
+		return CKR_SESSION_HANDLE_INVALID;
+	}
+
+	g_assert (pMechanism);
+	g_assert (ulCount);
+	g_assert (pTemplate);
+	g_assert (phKey);
+
+	attrs = lookup_object (session, hBaseKey);
+	if (!attrs) {
+		g_assert_not_reached ();
+		return CKR_WRAPPING_KEY_HANDLE_INVALID;
+	}
+
+	if (pMechanism->mechanism != CKM_DERIVE)
+		return CKR_MECHANISM_INVALID;
+
+	if (pMechanism->pParameter) {
+		if (pMechanism->ulParameterLen != 6 ||
+		    memcmp (pMechanism->pParameter, "derive", 6) != 0) {
+			g_assert_not_reached ();
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+	}
+
+	copy = gck_attributes_new ();
+	gck_attributes_add_string (copy, CKA_VALUE, "derived");
+	for (i = 0; i < ulCount; ++i)
+		gck_attributes_add_data (copy, pTemplate[i].type,
+		                          pTemplate[i].pValue,
+		                          pTemplate[i].ulValueLen);
+	for (i = 0; i < gck_attributes_count (attrs); ++i)
+		gck_attributes_add (copy, gck_attributes_at (attrs, i));
+	*phKey = ++unique_identifier;
+	if (gck_attributes_find_boolean (copy, CKA_TOKEN, &token) && token)
+		g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phKey), copy);
+	else
+		g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phKey), copy);
+
+	return CKR_OK;
+}
+
+static CK_RV
+test_C_SeedRandom (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+test_C_GenerateRandom (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData,
+                      CK_ULONG ulRandomLen)
+{
+	g_assert_not_reached (); /* Not yet used by library */
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_FUNCTION_LIST functionList = {
+	{ 2, 11 },	/* version */
+	test_C_Initialize,
+	test_C_Finalize,
+	test_C_GetInfo,
+	test_C_GetFunctionList,
+	test_C_GetSlotList,
+	test_C_GetSlotInfo,
+	test_C_GetTokenInfo,
+	test_C_GetMechanismList,
+	test_C_GetMechanismInfo,
+	test_C_InitToken,
+	test_C_InitPIN,
+	test_C_SetPIN,
+	test_C_OpenSession,
+	test_C_CloseSession,
+	test_C_CloseAllSessions,
+	test_C_GetSessionInfo,
+	test_C_GetOperationState,
+	test_C_SetOperationState,
+	test_C_Login,
+	test_C_Logout,
+	test_C_CreateObject,
+	test_C_CopyObject,
+	test_C_DestroyObject,
+	test_C_GetObjectSize,
+	test_C_GetAttributeValue,
+	test_C_SetAttributeValue,
+	test_C_FindObjectsInit,
+	test_C_FindObjects,
+	test_C_FindObjectsFinal,
+	test_C_EncryptInit,
+	test_C_Encrypt,
+	test_C_EncryptUpdate,
+	test_C_EncryptFinal,
+	test_C_DecryptInit,
+	test_C_Decrypt,
+	test_C_DecryptUpdate,
+	test_C_DecryptFinal,
+	test_C_DigestInit,
+	test_C_Digest,
+	test_C_DigestUpdate,
+	test_C_DigestKey,
+	test_C_DigestFinal,
+	test_C_SignInit,
+	test_C_Sign,
+	test_C_SignUpdate,
+	test_C_SignFinal,
+	test_C_SignRecoverInit,
+	test_C_SignRecover,
+	test_C_VerifyInit,
+	test_C_Verify,
+	test_C_VerifyUpdate,
+	test_C_VerifyFinal,
+	test_C_VerifyRecoverInit,
+	test_C_VerifyRecover,
+	test_C_DigestEncryptUpdate,
+	test_C_DecryptDigestUpdate,
+	test_C_SignEncryptUpdate,
+	test_C_DecryptVerifyUpdate,
+	test_C_GenerateKey,
+	test_C_GenerateKeyPair,
+	test_C_WrapKey,
+	test_C_UnwrapKey,
+	test_C_DeriveKey,
+	test_C_SeedRandom,
+	test_C_GenerateRandom,
+	test_C_GetFunctionStatus,
+	test_C_CancelFunction,
+	test_C_WaitForSlotEvent
+};
+
+CK_RV
+C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+	if (!list)
+		return CKR_ARGUMENTS_BAD;
+
+	*list = &functionList;
+	return CKR_OK;
+}
diff --git a/gck/tests/gck-test.h b/gck/tests/gck-test.h
new file mode 100644
index 0000000..078c962
--- /dev/null
+++ b/gck/tests/gck-test.h
@@ -0,0 +1,46 @@
+#ifndef GCK_TEST_H_
+#define GCK_TEST_H_
+
+#include "gck.h"
+
+#define FAIL_RES(res, e) do { \
+	g_assert ((res) ? FALSE : TRUE); \
+	g_assert ((e) && (e)->message && "error should be set"); \
+	g_clear_error (&e); \
+	} while (0)
+
+#define SUCCESS_RES(res, err) do { \
+	if (!(res)) g_printerr ("error: %s\n", err && err->message ? err->message : ""); \
+	g_assert ((res) ? TRUE : FALSE && "should have succeeded"); \
+	g_clear_error (&err); \
+	} while(0)
+
+/*
+ * Some dumb crypto mechanisms for simple testing.
+ *
+ * CKM_CAPITALIZE (encrypt/decrypt)
+ *     capitalizes to encrypt
+ *     lowercase to decrypt
+ *
+ * CKM_PREFIX (sign/verify)
+ *     sign prefixes data with key label
+ *     verify unprefixes data with key label.
+ *
+ * CKM_GENERATE (generate-pair)
+ *     generates a pair of keys, mechanism param should be 'generate'
+ *
+ * CKM_WRAP (wrap key)
+ *     wraps key by returning value, mechanism param should be 'wrap'
+ *
+ * CKM_DERIVE (derive-key)
+ *     derives key by setting value to 'derived'.
+ *     mechanism param should be 'derive'
+ */
+
+#define CKM_CAPITALIZE    (CKM_VENDOR_DEFINED | 1)
+#define CKM_PREFIX        (CKM_VENDOR_DEFINED | 2)
+#define CKM_GENERATE      (CKM_VENDOR_DEFINED | 3)
+#define CKM_WRAP          (CKM_VENDOR_DEFINED | 4)
+#define CKM_DERIVE        (CKM_VENDOR_DEFINED | 5)
+
+#endif /* GCK_TEST_H_ */
diff --git a/gck/tests/test-gck-attributes.c b/gck/tests/test-gck-attributes.c
new file mode 100644
index 0000000..24e8ee3
--- /dev/null
+++ b/gck/tests/test-gck-attributes.c
@@ -0,0 +1,528 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+
+#define ATTR_TYPE 55
+#define ATTR_DATA "TEST DATA"
+#define N_ATTR_DATA ((gsize)9)
+
+DEFINE_TEST(init_memory)
+{
+	GckAttribute attr;
+
+	g_assert (sizeof (attr) == sizeof (CK_ATTRIBUTE));
+
+	gck_attribute_init (&attr, ATTR_TYPE, ATTR_DATA, N_ATTR_DATA);
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == N_ATTR_DATA);
+	g_assert (memcmp (attr.value, ATTR_DATA, attr.length) == 0);
+
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(init_boolean)
+{
+	GckAttribute attr;
+
+	gck_attribute_init_boolean (&attr, ATTR_TYPE, TRUE);
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == sizeof (CK_BBOOL));
+	g_assert (*((CK_BBOOL*)attr.value) == CK_TRUE);
+
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(init_date)
+{
+	GckAttribute attr;
+	CK_DATE ck_date;
+	GDate *date;
+
+	date = g_date_new_dmy(05, 06, 1960);
+	memcpy (ck_date.year, "1960", 4);
+	memcpy (ck_date.month, "06", 2);
+	memcpy (ck_date.day, "05", 2);
+	gck_attribute_init_date (&attr, ATTR_TYPE, date);
+	g_date_free (date);
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == sizeof (CK_DATE));
+	g_assert (memcmp (attr.value, &ck_date, attr.length) == 0);
+
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(init_ulong)
+{
+	GckAttribute attr;
+
+	gck_attribute_init_ulong (&attr, ATTR_TYPE, 88);
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == sizeof (CK_ULONG));
+	g_assert (*((CK_ULONG*)attr.value) == 88);
+
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(init_string)
+{
+	GckAttribute attr;
+
+	gck_attribute_init_string (&attr, ATTR_TYPE, "a test string");
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == strlen ("a test string"));
+	g_assert (memcmp (attr.value, "a test string", attr.length) == 0);
+
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(init_invalid)
+{
+	GckAttribute attr;
+
+	gck_attribute_init_invalid (&attr, ATTR_TYPE);
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == (gulong)-1);
+	g_assert (attr.value == NULL);
+
+	g_assert (gck_attribute_is_invalid (&attr));
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(init_empty)
+{
+	GckAttribute attr;
+
+	gck_attribute_init_empty (&attr, ATTR_TYPE);
+	g_assert (attr.type == ATTR_TYPE);
+	g_assert (attr.length == 0);
+	g_assert (attr.value == NULL);
+
+	gck_attribute_clear (&attr);
+}
+
+DEFINE_TEST(new_memory)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new (ATTR_TYPE, ATTR_DATA, N_ATTR_DATA);
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == N_ATTR_DATA);
+	g_assert (memcmp (attr->value, ATTR_DATA, attr->length) == 0);
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(new_boolean)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_boolean (ATTR_TYPE, TRUE);
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == sizeof (CK_BBOOL));
+	g_assert (*((CK_BBOOL*)attr->value) == CK_TRUE);
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(new_date)
+{
+	GckAttribute *attr;
+	CK_DATE ck_date;
+	GDate *date;
+
+	date = g_date_new_dmy(05, 06, 1800);
+	memcpy (ck_date.year, "1800", 4);
+	memcpy (ck_date.month, "06", 2);
+	memcpy (ck_date.day, "05", 2);
+	attr = gck_attribute_new_date (ATTR_TYPE, date);
+	g_date_free (date);
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == sizeof (CK_DATE));
+	g_assert (memcmp (attr->value, &ck_date, attr->length) == 0);
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(new_ulong)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_ulong (ATTR_TYPE, 88);
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == sizeof (CK_ULONG));
+	g_assert (*((CK_ULONG*)attr->value) == 88);
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(new_string)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_string (ATTR_TYPE, "a test string");
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == strlen ("a test string"));
+	g_assert (memcmp (attr->value, "a test string", attr->length) == 0);
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(new_invalid)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_invalid (ATTR_TYPE);
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == (gulong)-1);
+	g_assert (attr->value == NULL);
+
+	g_assert (gck_attribute_is_invalid (attr));
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(new_empty)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_empty (ATTR_TYPE);
+	g_assert (attr->type == ATTR_TYPE);
+	g_assert (attr->length == 0);
+	g_assert (attr->value == NULL);
+
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(get_boolean)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_boolean (ATTR_TYPE, TRUE);
+	g_assert (gck_attribute_get_boolean (attr) == TRUE);
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(get_date)
+{
+	GckAttribute *attr;
+	CK_DATE ck_date;
+	GDate date, date2;
+
+	g_date_set_dmy(&date, 05, 06, 1800);
+	memcpy (ck_date.year, "1800", 4);
+	memcpy (ck_date.month, "06", 2);
+	memcpy (ck_date.day, "05", 2);
+	attr = gck_attribute_new_date (ATTR_TYPE, &date);
+	gck_attribute_get_date (attr, &date2);
+	g_assert (g_date_compare (&date, &date2) == 0);
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(get_ulong)
+{
+	GckAttribute *attr;
+
+	attr = gck_attribute_new_ulong (ATTR_TYPE, 88);
+	g_assert (gck_attribute_get_ulong (attr) == 88);
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(get_string)
+{
+	GckAttribute *attr;
+	gchar *value;
+
+	attr = gck_attribute_new_string (ATTR_TYPE, "a test string");
+	value = gck_attribute_get_string (attr);
+	g_assert (strcmp ("a test string", value) == 0);
+	g_free (value);
+	gck_attribute_free (attr);
+
+	/* Should be able to store null strings */
+	attr = gck_attribute_new_string (ATTR_TYPE, NULL);
+	value = gck_attribute_get_string (attr);
+	g_assert (value == NULL);
+	gck_attribute_free (attr);
+}
+
+DEFINE_TEST(dup_attribute)
+{
+	GckAttribute attr, *dup;
+
+	gck_attribute_init_ulong (&attr, ATTR_TYPE, 88);
+	dup = gck_attribute_dup (&attr);
+	gck_attribute_clear (&attr);
+	g_assert (gck_attribute_get_ulong (dup) == 88);
+	g_assert (dup->type == ATTR_TYPE);
+	gck_attribute_free (dup);
+
+	/* Should be able to dup null */
+	dup = gck_attribute_dup (NULL);
+	g_assert (dup == NULL);
+}
+
+DEFINE_TEST(copy_attribute)
+{
+	GckAttribute attr, copy;
+
+	gck_attribute_init_ulong (&attr, ATTR_TYPE, 88);
+	gck_attribute_init_copy (&copy, &attr);
+	gck_attribute_clear (&attr);
+	g_assert (gck_attribute_get_ulong (&copy) == 88);
+	g_assert (copy.type == ATTR_TYPE);
+	gck_attribute_clear (&copy);
+}
+
+DEFINE_TEST(new_attributes)
+{
+	GckAttributes *attrs;
+
+	attrs = gck_attributes_new ();
+	g_assert (attrs != NULL);
+	g_assert (gck_attributes_count (attrs) == 0);
+
+	gck_attributes_ref (attrs);
+	gck_attributes_unref (attrs);
+
+	gck_attributes_unref (attrs);
+
+	/* Can unref NULL */
+	gck_attributes_unref (NULL);
+}
+
+static void
+test_attributes_contents (GckAttributes *attrs, gboolean extras)
+{
+	GckAttribute *attr;
+	gchar *value;
+	GDate date, *check;
+	guint count;
+
+	g_assert (attrs != NULL);
+	count = extras ? 7 : 5;
+	g_assert_cmpuint (gck_attributes_count (attrs), ==, count);
+
+	attr = gck_attributes_at (attrs, 0);
+	g_assert (attr->type == 0);
+	g_assert (gck_attribute_get_boolean (attr) == TRUE);
+
+	attr = gck_attributes_at (attrs, 1);
+	g_assert (attr->type == 101);
+	g_assert (gck_attribute_get_ulong (attr) == 888);
+
+	attr = gck_attributes_at (attrs, 2);
+	g_assert (attr->type == 202);
+	value = gck_attribute_get_string (attr);
+	g_assert (strcmp (value, "string") == 0);
+	g_free (value);
+
+	attr = gck_attributes_at (attrs, 3);
+	g_assert (attr->type == 303);
+	check = g_date_new_dmy (11, 12, 2008);
+	gck_attribute_get_date (attr, &date);
+	g_assert (g_date_compare (&date, check) == 0);
+	g_date_free (check);
+
+	attr = gck_attributes_at (attrs, 4);
+	g_assert (attr->type == 404);
+	g_assert (attr->length == N_ATTR_DATA);
+	g_assert (memcmp (attr->value, ATTR_DATA, N_ATTR_DATA) == 0);
+
+	if (!extras)
+		return;
+
+	attr = gck_attributes_at (attrs, 5);
+	g_assert (attr->type == 505);
+	g_assert (attr->length == (gulong)-1);
+	g_assert (attr->value == NULL);
+	g_assert (gck_attribute_is_invalid (attr));
+
+	attr = gck_attributes_at (attrs, 6);
+	g_assert (attr->type == 606);
+	g_assert (attr->length == 0);
+	g_assert (attr->value == NULL);
+}
+
+DEFINE_TEST(newv_attributes)
+{
+	GDate *date = g_date_new_dmy (11, 12, 2008);
+	GckAttributes *attrs;
+	attrs = gck_attributes_newv (0UL, GCK_BOOLEAN, TRUE,
+	                              101UL, GCK_ULONG, 888UL,
+	                              202UL, GCK_STRING, "string",
+	                              303UL, GCK_DATE, date,
+	                              404UL, N_ATTR_DATA, ATTR_DATA,
+	                              GCK_INVALID);
+	g_date_free (date);
+
+	test_attributes_contents (attrs, FALSE);
+	gck_attributes_unref (attrs);
+
+	/* An empty one */
+	attrs = gck_attributes_newv (GCK_INVALID);
+	gck_attributes_unref (attrs);
+}
+
+DEFINE_TEST(new_empty_attributes)
+{
+	GckAttributes *attrs = gck_attributes_new_empty (101UL, 202UL, 303UL, 404UL, GCK_INVALID);
+	GckAttribute *attr;
+	guint i;
+
+	g_assert_cmpuint (gck_attributes_count (attrs), ==, 4);
+	for (i = 0; i < gck_attributes_count (attrs); ++i) {
+		attr = gck_attributes_at (attrs, i);
+		g_assert (attr->type == ((i + 1) * 100) + i + 1);
+		g_assert (attr->value == NULL);
+		g_assert (attr->length == 0);
+	}
+}
+
+static GckAttributes*
+help_attributes_valist (int dummy, ...)
+{
+	GckAttributes *attrs;
+	va_list va;
+
+	va_start (va, dummy);
+	attrs = gck_attributes_new_valist (NULL, va);
+	va_end (va);
+
+	return attrs;
+}
+
+DEFINE_TEST(new_valist_attributes)
+{
+	GckAttributes *attrs;
+	GDate *date = g_date_new_dmy (11, 12, 2008);
+
+	attrs = help_attributes_valist (232434243, /* Not used */
+	                                0UL, GCK_BOOLEAN, TRUE,
+	                                101UL, GCK_ULONG, 888UL,
+	                                202UL, GCK_STRING, "string",
+	                                303UL, GCK_DATE, date,
+	                                404UL, N_ATTR_DATA, ATTR_DATA,
+	                                GCK_INVALID);
+
+	g_date_free (date);
+	test_attributes_contents (attrs, FALSE);
+	gck_attributes_unref (attrs);
+}
+
+#if 0
+DEFINE_ABORT(bad_length)
+{
+	GckAttributes *attrs;
+
+	/* We should catch this with a warning */
+	attrs = gck_attributes_newv (1UL, G_MAXSSIZE + 500U, GCK_ULONG, "invalid data",
+	                              GCK_INVALID);
+
+	gck_attributes_unref (attrs);
+}
+#endif
+
+DEFINE_TEST(add_data_attributes)
+{
+	GckAttributes *attrs;
+	GDate *date = g_date_new_dmy (11, 12, 2008);
+	attrs = gck_attributes_new ();
+	gck_attributes_add_boolean (attrs, 0UL, TRUE);
+	gck_attributes_add_ulong (attrs, 101UL, 888);
+	gck_attributes_add_string (attrs, 202UL, "string");
+	gck_attributes_add_date (attrs, 303UL, date);
+	g_date_free (date);
+	gck_attributes_add_data (attrs, 404UL, ATTR_DATA, N_ATTR_DATA);
+	gck_attributes_add_invalid (attrs, 505UL);
+	gck_attributes_add_empty (attrs, 606UL);
+	test_attributes_contents (attrs, TRUE);
+	gck_attributes_unref (attrs);
+}
+
+DEFINE_TEST(add_attributes)
+{
+	GckAttributes *attrs;
+	GckAttribute attr;
+
+	GDate *date = g_date_new_dmy (11, 12, 2008);
+	attrs = gck_attributes_new ();
+
+	gck_attribute_init_boolean (&attr, 0UL, TRUE);
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+
+	gck_attribute_init_ulong (&attr, 101UL, 888);
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+
+	gck_attribute_init_string (&attr, 202UL, "string");
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+
+	gck_attribute_init_date (&attr, 303UL, date);
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+	g_date_free (date);
+
+	gck_attribute_init (&attr, 404UL, ATTR_DATA, N_ATTR_DATA);
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+
+	gck_attribute_init_invalid (&attr, 505UL);
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+
+	gck_attribute_init_empty (&attr, 606UL);
+	gck_attributes_add (attrs, &attr);
+	gck_attribute_clear (&attr);
+
+	test_attributes_contents (attrs, TRUE);
+	gck_attributes_unref (attrs);
+}
+
+
+DEFINE_TEST(find_attributes)
+{
+	GckAttribute *attr;
+	GDate check, *date = g_date_new_dmy (13, 12, 2008);
+	gboolean bvalue, ret;
+	gulong uvalue;
+	gchar *svalue;
+
+	GckAttributes *attrs;
+	attrs = gck_attributes_newv (0UL, GCK_BOOLEAN, TRUE,
+	                              101UL, GCK_ULONG, 888UL,
+	                              202UL, GCK_STRING, "string",
+	                              303UL, GCK_DATE, date,
+	                              404UL, N_ATTR_DATA, ATTR_DATA,
+	                              GCK_INVALID);
+
+	attr = gck_attributes_find (attrs, 404);
+	g_assert (attr != NULL);
+	g_assert (attr->length == N_ATTR_DATA);
+	g_assert (memcmp (attr->value, ATTR_DATA, N_ATTR_DATA) == 0);
+
+	ret = gck_attributes_find_boolean (attrs, 0UL, &bvalue);
+	g_assert (ret == TRUE);
+	g_assert (bvalue == TRUE);
+
+	ret = gck_attributes_find_ulong (attrs, 101UL, &uvalue);
+	g_assert (ret == TRUE);
+	g_assert (uvalue == 888);
+
+	ret = gck_attributes_find_string (attrs, 202UL, &svalue);
+	g_assert (ret == TRUE);
+	g_assert (svalue != NULL);
+	g_assert (strcmp (svalue, "string") == 0);
+	g_free (svalue);
+
+	ret = gck_attributes_find_date (attrs, 303UL, &check);
+	g_assert (ret == TRUE);
+	g_assert (g_date_compare (date, &check) == 0);
+
+	gck_attributes_unref (attrs);
+	g_date_free (date);
+}
diff --git a/gck/tests/test-gck-crypto.c b/gck/tests/test-gck-crypto.c
new file mode 100644
index 0000000..f03b7e7
--- /dev/null
+++ b/gck/tests/test-gck-crypto.c
@@ -0,0 +1,595 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test-suite.h"
+
+#include <glib.h>
+
+#include "gck-test.h"
+
+static GckModule *module = NULL;
+static GckSlot *slot = NULL;
+static GckSession *session = NULL;
+
+DEFINE_SETUP(crypto_session)
+{
+	GError *err = NULL;
+	GList *slots;
+
+	/* Successful load */
+	module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err);
+	SUCCESS_RES (module, err);
+
+	slots = gck_module_get_slots (module, TRUE);
+	g_assert (slots != NULL);
+
+	slot = GCK_SLOT (slots->data);
+	g_object_ref (slot);
+	gck_list_unref_free (slots);
+
+	session = gck_slot_open_session (slot, 0, &err);
+	SUCCESS_RES(session, err);
+}
+
+DEFINE_TEARDOWN(crypto_session)
+{
+	g_object_unref (session);
+	g_object_unref (slot);
+	g_object_unref (module);
+}
+
+static void
+fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+	*((GAsyncResult**)user_data) = result;
+	g_object_ref (result);
+	testing_wait_stop ();
+}
+
+static GckObject*
+find_key (GckSession *session, CK_ATTRIBUTE_TYPE method, CK_MECHANISM_TYPE mech)
+{
+	GList *objects, *l;
+	GckObject *object = NULL;
+	CK_MECHANISM_TYPE_PTR mechs;
+	gsize n_mechs;
+
+	objects = gck_session_find_objects (session, NULL, method, GCK_BOOLEAN, TRUE, GCK_INVALID);
+	g_assert (objects);
+
+	for (l = objects; l; l = g_list_next (l)) {
+		gck_object_set_session (l->data, session);
+		if (mech) {
+			mechs = gck_object_get_data (l->data, CKA_ALLOWED_MECHANISMS, &n_mechs, NULL);
+			g_assert (mechs);
+			g_assert (n_mechs == sizeof (CK_MECHANISM_TYPE));
+			/* We know all of them only have one allowed mech */
+			if (*mechs != mech)
+				continue;
+		}
+		object = l->data;
+		g_object_ref (object);
+		break;
+	}
+
+	gck_list_unref_free (objects);
+	return object;
+}
+
+static GckObject*
+find_key_with_value (GckSession *session, const gchar *value)
+{
+	GList *objects;
+	GckObject *object;
+
+	objects = gck_session_find_objects (session, NULL, CKA_VALUE, GCK_STRING, value, GCK_INVALID);
+	g_assert (objects);
+
+	object = g_object_ref (objects->data);
+	gck_list_unref_free (objects);
+	return object;
+}
+
+static void
+check_key_with_value (GckSession *session, GckObject *key, CK_OBJECT_CLASS klass, const gchar *value)
+{
+	GckAttributes *attrs;
+	GckAttribute *attr;
+	gulong check;
+
+	gck_object_set_session (key, session);
+	attrs = gck_object_get (key, NULL, CKA_CLASS, CKA_VALUE, GCK_INVALID);
+	g_assert (attrs);
+
+	if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &check))
+		g_assert_not_reached ();
+	g_assert (check == klass);
+
+	attr = gck_attributes_find (attrs, CKA_VALUE);
+	g_assert (attr);
+	g_assert (!gck_attribute_is_invalid (attr));
+	g_assert_cmpsize (attr->length, ==, strlen (value));
+	g_assert (memcmp (attr->value, value, attr->length) == 0);
+
+	gck_attributes_unref (attrs);
+}
+
+static gboolean
+authenticate_object (GckSlot *module, GckObject *object, gchar *label, gchar **password)
+{
+	g_assert (GCK_IS_MODULE (module));
+	g_assert (GCK_IS_OBJECT (object));
+	g_assert (password);
+	g_assert (!*password);
+
+	*password = g_strdup ("booo");
+	return TRUE;
+}
+
+DEFINE_TEST(encrypt)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *key;
+	guchar *output;
+	gsize n_output;
+
+	mech = gck_mechanism_new (CKM_CAPITALIZE);
+
+	/* Find the right key */
+	key = find_key (session, CKA_ENCRYPT, CKM_CAPITALIZE);
+	g_assert (key);
+
+	/* Simple one */
+	output = gck_session_encrypt (session, key, CKM_CAPITALIZE, (const guchar*)"blah blah", 10, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert (n_output == 10);
+	g_assert_cmpstr ((gchar*)output, ==, "BLAH BLAH");
+	g_free (output);
+
+	/* Full one */
+	output = gck_session_encrypt_full (session, key, mech, (const guchar*)"blah blah", 10, &n_output, NULL, &error);
+	SUCCESS_RES (output, error);
+	g_assert (n_output == 10);
+	g_assert_cmpstr ((gchar*)output, ==, "BLAH BLAH");
+	g_free (output);
+
+	/* Asynchronous one */
+	gck_session_encrypt_async (session, key, mech, (const guchar*)"second chance", 14, NULL, fetch_async_result, &result);
+
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	output = gck_session_encrypt_finish (session, result, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert (n_output == 14);
+	g_assert_cmpstr ((gchar*)output, ==, "SECOND CHANCE");
+	g_free (output);
+
+	gck_mechanism_unref (mech);
+	g_object_unref (result);
+	g_object_unref (key);
+}
+
+DEFINE_TEST(decrypt)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *key;
+	guchar *output;
+	gsize n_output;
+
+	mech = gck_mechanism_new (CKM_CAPITALIZE);
+
+	/* Find the right key */
+	key = find_key (session, CKA_DECRYPT, CKM_CAPITALIZE);
+	g_assert (key);
+
+	/* Simple one */
+	output = gck_session_decrypt (session, key, CKM_CAPITALIZE, (const guchar*)"FRY???", 7, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert (n_output == 7);
+	g_assert_cmpstr ((gchar*)output, ==, "fry???");
+	g_free (output);
+
+	/* Full one */
+	output = gck_session_decrypt_full (session, key, mech, (const guchar*)"TENNIS instructor", 18, &n_output, NULL, &error);
+	SUCCESS_RES (output, error);
+	g_assert (n_output == 18);
+	g_assert_cmpstr ((gchar*)output, ==, "tennis instructor");
+	g_free (output);
+
+	/* Asynchronous one */
+	gck_session_decrypt_async (session, key, mech, (const guchar*)"FAT CHANCE", 11, NULL, fetch_async_result, &result);
+
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	output = gck_session_decrypt_finish (session, result, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert (n_output == 11);
+	g_assert_cmpstr ((gchar*)output, ==, "fat chance");
+	g_free (output);
+
+	gck_mechanism_unref (mech);
+	g_object_unref (result);
+	g_object_unref (key);
+}
+
+DEFINE_TEST(login_context_specific)
+{
+	/* The test module won't let us sign without doing a login, check that */
+
+	GError *error = NULL;
+	GckObject *key;
+	guchar *output;
+	gsize n_output;
+
+	/* Find the right key */
+	key = find_key (session, CKA_SIGN, CKM_PREFIX);
+	g_assert (key);
+
+	/* Simple one */
+	output = gck_session_sign (session, key, CKM_PREFIX, (const guchar*)"TV Monster", 11, &n_output, &error);
+	g_assert (error && error->code == CKR_USER_NOT_LOGGED_IN);
+	FAIL_RES (output, error);
+	g_assert (output == NULL);
+
+	g_object_unref (key);
+}
+
+DEFINE_TEST(sign)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *key;
+	guchar *output;
+	gsize n_output;
+
+	mech = gck_mechanism_new_with_param (CKM_PREFIX, "my-prefix:", 10);
+
+	/* Enable auto-login on this session, see previous test */
+	gck_module_set_auto_authenticate (module, TRUE);
+	g_signal_connect (module, "authenticate-object", G_CALLBACK (authenticate_object), NULL);
+
+	/* Find the right key */
+	key = find_key (session, CKA_SIGN, CKM_PREFIX);
+	g_assert (key);
+
+	/* Simple one */
+	output = gck_session_sign (session, key, CKM_PREFIX, (const guchar*)"Labarbara", 10, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert_cmpuint (n_output, ==, 24);
+	g_assert_cmpstr ((gchar*)output, ==, "signed-prefix:Labarbara");
+	g_free (output);
+
+	/* Full one */
+	output = gck_session_sign_full (session, key, mech, (const guchar*)"Labarbara", 10, &n_output, NULL, &error);
+	SUCCESS_RES (output, error);
+	g_assert_cmpuint (n_output, ==, 20);
+	g_assert_cmpstr ((gchar*)output, ==, "my-prefix:Labarbara");
+	g_free (output);
+
+	/* Asynchronous one */
+	gck_session_sign_async (session, key, mech, (const guchar*)"Conrad", 7, NULL, fetch_async_result, &result);
+
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	output = gck_session_sign_finish (session, result, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert_cmpuint (n_output, ==, 17);
+	g_assert_cmpstr ((gchar*)output, ==, "my-prefix:Conrad");
+	g_free (output);
+
+	gck_mechanism_unref (mech);
+	g_object_unref (result);
+	g_object_unref (key);
+}
+
+DEFINE_TEST(verify)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *key;
+	gboolean ret;
+
+	mech = gck_mechanism_new_with_param (CKM_PREFIX, "my-prefix:", 10);
+
+	/* Enable auto-login on this session, shouldn't be needed */
+	gck_module_set_auto_authenticate (module, TRUE);
+	g_signal_connect (module, "authenticate-object", G_CALLBACK (authenticate_object), NULL);
+
+	/* Find the right key */
+	key = find_key (session, CKA_VERIFY, CKM_PREFIX);
+	g_assert (key);
+
+	/* Simple one */
+	ret = gck_session_verify (session, key, CKM_PREFIX, (const guchar*)"Labarbara", 10,
+	                           (const guchar*)"signed-prefix:Labarbara", 24, &error);
+	SUCCESS_RES (ret, error);
+
+	/* Full one */
+	ret = gck_session_verify_full (session, key, mech, (const guchar*)"Labarbara", 10,
+	                                (const guchar*)"my-prefix:Labarbara", 20, NULL, &error);
+	SUCCESS_RES (ret, error);
+
+	/* Failure one */
+	ret = gck_session_verify_full (session, key, mech, (const guchar*)"Labarbara", 10,
+	                                (const guchar*)"my-prefix:Loborboro", 20, NULL, &error);
+	FAIL_RES (ret, error);
+
+	/* Asynchronous one */
+	gck_session_verify_async (session, key, mech, (const guchar*)"Labarbara", 10,
+	                           (const guchar*)"my-prefix:Labarbara", 20, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	ret = gck_session_verify_finish (session, result, &error);
+	SUCCESS_RES (ret, error);
+	g_object_unref (result);
+
+	/* Asynchronous failure */
+	result = NULL;
+	gck_session_verify_async (session, key, mech, (const guchar*)"Labarbara", 10,
+	                           (const guchar*)"my-prefix:Labarxoro", 20, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	ret = gck_session_verify_finish (session, result, &error);
+	FAIL_RES (ret, error);
+	g_object_unref (result);
+
+	gck_mechanism_unref (mech);
+	g_object_unref (key);
+}
+
+DEFINE_TEST(generate_key_pair)
+{
+	GckAttributes *pub_attrs, *prv_attrs;
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *pub_key, *prv_key;
+	gboolean ret;
+
+	mech = gck_mechanism_new_with_param (CKM_GENERATE, "generate", 9);
+
+	pub_attrs = gck_attributes_new ();
+	gck_attributes_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+	prv_attrs = gck_attributes_new ();
+	gck_attributes_add_ulong (prv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+
+	/* Full One*/
+	ret = gck_session_generate_key_pair_full (session, mech, pub_attrs, prv_attrs,
+	                                           &pub_key, &prv_key, NULL, &error);
+	SUCCESS_RES (ret, error);
+	g_object_unref (pub_key);
+	g_object_unref (prv_key);
+
+	/* Failure one */
+	mech->type = 0;
+	pub_key = prv_key = NULL;
+	ret = gck_session_generate_key_pair_full (session, mech, pub_attrs, prv_attrs,
+	                                           &pub_key, &prv_key, NULL, &error);
+	FAIL_RES (ret, error);
+	g_assert (pub_key == NULL);
+	g_assert (prv_key == NULL);
+
+	/* Asynchronous one */
+	mech->type = CKM_GENERATE;
+	gck_session_generate_key_pair_async (session, mech, pub_attrs, prv_attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	ret = gck_session_generate_key_pair_finish (session, result, &pub_key, &prv_key, &error);
+	SUCCESS_RES (ret, error);
+	g_object_unref (result);
+	g_object_unref (pub_key);
+	g_object_unref (prv_key);
+
+	/* Asynchronous failure */
+	result = NULL;
+	mech->type = 0;
+	pub_key = prv_key = NULL;
+	gck_session_generate_key_pair_async (session, mech, pub_attrs, prv_attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	ret = gck_session_generate_key_pair_finish (session, result, &pub_key, &prv_key, &error);
+	FAIL_RES (ret, error);
+	g_object_unref (result);
+	g_assert (pub_key == NULL);
+	g_assert (prv_key == NULL);
+
+	gck_mechanism_unref (mech);
+	gck_attributes_unref (pub_attrs);
+	gck_attributes_unref (prv_attrs);
+}
+
+DEFINE_TEST(wrap_key)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *wrapper, *wrapped;
+	gpointer output;
+	gsize n_output;
+
+	mech = gck_mechanism_new_with_param (CKM_WRAP, "wrap", 4);
+	wrapper = find_key (session, CKA_WRAP, 0);
+	wrapped = find_key_with_value (session, "value");
+
+	/* Simple One */
+	output = gck_session_wrap_key (session, wrapper, CKM_WRAP, wrapped, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert (output);
+	g_assert_cmpsize (n_output, ==, 5);
+	g_assert (memcmp (output, "value", 5) == 0);
+	g_free (output);
+
+	/* Full One*/
+	output = gck_session_wrap_key_full (session, wrapper, mech, wrapped, &n_output, NULL, &error);
+	SUCCESS_RES (output, error);
+	g_assert_cmpsize (n_output, ==, 5);
+	g_assert (memcmp (output, "value", 5) == 0);
+	g_free (output);
+
+	/* Failure one */
+	mech->type = 0;
+	n_output = 0;
+	output = gck_session_wrap_key_full (session, wrapper, mech, wrapped, &n_output, NULL, &error);
+	FAIL_RES (output, error);
+	g_assert_cmpsize (n_output, ==, 0);
+
+	/* Asynchronous one */
+	mech->type = CKM_WRAP;
+	gck_session_wrap_key_async (session, wrapper, mech, wrapped, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	output = gck_session_wrap_key_finish (session, result, &n_output, &error);
+	SUCCESS_RES (output, error);
+	g_assert_cmpsize (n_output, ==, 5);
+	g_assert (memcmp (output, "value", 5) == 0);
+	g_object_unref (result);
+	g_free (output);
+
+	/* Asynchronous failure */
+	result = NULL;
+	mech->type = 0;
+	n_output = 0;
+	gck_session_wrap_key_async (session, wrapper, mech, wrapped, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	output = gck_session_wrap_key_finish (session, result, &n_output, &error);
+	FAIL_RES (output, error);
+	g_assert_cmpsize (n_output, ==, 0);
+	g_object_unref (result);
+
+	g_object_unref (wrapper);
+	g_object_unref (wrapped);
+	gck_mechanism_unref (mech);
+}
+
+DEFINE_TEST(unwrap_key)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *wrapper, *unwrapped;
+	GckAttributes *attrs;
+
+	mech = gck_mechanism_new_with_param (CKM_WRAP, "wrap", 4);
+	wrapper = find_key (session, CKA_UNWRAP, 0);
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID);
+
+	/* Simple One */
+	unwrapped = gck_session_unwrap_key (session, wrapper, CKM_WRAP, "special", 7, &error,
+	                                     CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID);
+	SUCCESS_RES (unwrapped, error);
+	g_assert (GCK_IS_OBJECT (unwrapped));
+	check_key_with_value (session, unwrapped, CKO_SECRET_KEY, "special");
+	g_object_unref (unwrapped);
+
+	/* Full One*/
+	unwrapped = gck_session_unwrap_key_full (session, wrapper, mech, "special", 7, attrs, NULL, &error);
+	SUCCESS_RES (unwrapped, error);
+	g_assert (GCK_IS_OBJECT (unwrapped));
+	check_key_with_value (session, unwrapped, CKO_SECRET_KEY, "special");
+	g_object_unref (unwrapped);
+
+	/* Failure one */
+	mech->type = 0;
+	unwrapped = gck_session_unwrap_key_full (session, wrapper, mech, "special", 7, attrs, NULL, &error);
+	FAIL_RES (unwrapped, error);
+
+	/* Asynchronous one */
+	mech->type = CKM_WRAP;
+	gck_session_unwrap_key_async (session, wrapper, mech, "special", 7, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	unwrapped = gck_session_unwrap_key_finish (session, result, &error);
+	SUCCESS_RES (unwrapped, error);
+	g_assert (GCK_IS_OBJECT (unwrapped));
+	check_key_with_value (session, unwrapped, CKO_SECRET_KEY, "special");
+	g_object_unref (unwrapped);
+	g_object_unref (result);
+
+	/* Asynchronous failure */
+	result = NULL;
+	mech->type = 0;
+	gck_session_unwrap_key_async (session, wrapper, mech, "special", 6, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	unwrapped = gck_session_unwrap_key_finish (session, result, &error);
+	FAIL_RES (unwrapped, error);
+	g_object_unref (result);
+
+	g_object_unref (wrapper);
+	gck_attributes_unref (attrs);
+	gck_mechanism_unref (mech);
+}
+
+DEFINE_TEST(derive_key)
+{
+	GckMechanism *mech;
+	GError *error = NULL;
+	GAsyncResult *result = NULL;
+	GckObject *wrapper, *derived;
+	GckAttributes *attrs;
+
+	mech = gck_mechanism_new_with_param (CKM_DERIVE, "derive", 6);
+	wrapper = find_key (session, CKA_DERIVE, 0);
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID);
+
+	/* Simple One */
+	derived = gck_session_derive_key (session, wrapper, CKM_DERIVE, &error,
+	                                   CKA_CLASS, GCK_ULONG, CKO_SECRET_KEY, GCK_INVALID);
+	SUCCESS_RES (derived, error);
+	g_assert (GCK_IS_OBJECT (derived));
+g_printerr ("derived is: %lu", gck_object_get_handle (derived));
+	check_key_with_value (session, derived, CKO_SECRET_KEY, "derived");
+	g_object_unref (derived);
+
+	/* Full One*/
+	derived = gck_session_derive_key_full (session, wrapper, mech, attrs, NULL, &error);
+	SUCCESS_RES (derived, error);
+	g_assert (GCK_IS_OBJECT (derived));
+	check_key_with_value (session, derived, CKO_SECRET_KEY, "derived");
+	g_object_unref (derived);
+
+	/* Failure one */
+	mech->type = 0;
+	derived = gck_session_derive_key_full (session, wrapper, mech, attrs, NULL, &error);
+	FAIL_RES (derived, error);
+
+	/* Asynchronous one */
+	mech->type = CKM_DERIVE;
+	gck_session_derive_key_async (session, wrapper, mech, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	derived = gck_session_derive_key_finish (session, result, &error);
+	SUCCESS_RES (derived, error);
+	g_assert (GCK_IS_OBJECT (derived));
+	check_key_with_value (session, derived, CKO_SECRET_KEY, "derived");
+	g_object_unref (derived);
+	g_object_unref (result);
+
+	/* Asynchronous failure */
+	result = NULL;
+	mech->type = 0;
+	gck_session_derive_key_async (session, wrapper, mech, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	derived = gck_session_derive_key_finish (session, result, &error);
+	FAIL_RES (derived, error);
+	g_object_unref (result);
+
+	g_object_unref (wrapper);
+	gck_attributes_unref (attrs);
+	gck_mechanism_unref (mech);
+}
diff --git a/gck/tests/test-gck-mechanism.c b/gck/tests/test-gck-mechanism.c
new file mode 100644
index 0000000..6667103
--- /dev/null
+++ b/gck/tests/test-gck-mechanism.c
@@ -0,0 +1,60 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+
+#define MECH_TYPE 55
+#define MECH_DATA "TEST DATA"
+#define N_MECH_DATA ((gsize)9)
+
+DEFINE_TEST(mech_new)
+{
+	GckMechanism *mech;
+
+	mech = gck_mechanism_new (MECH_TYPE);
+
+	g_assert (mech);
+	g_assert (mech->type == MECH_TYPE);
+	g_assert (mech->parameter == NULL);
+	g_assert (mech->n_parameter == 0);
+
+	gck_mechanism_unref (mech);
+}
+
+DEFINE_TEST(mech_new_with_param)
+{
+	GckMechanism *mech;
+	gpointer parameter = MECH_DATA;
+
+	mech = gck_mechanism_new_with_param (MECH_TYPE, parameter, N_MECH_DATA);
+
+	g_assert (mech);
+	g_assert (mech->type == MECH_TYPE);
+	g_assert (mech->parameter != NULL);
+	g_assert (mech->parameter != parameter); /* Copied */
+	g_assert (mech->n_parameter == N_MECH_DATA);
+	g_assert (memcmp (mech->parameter, MECH_DATA, N_MECH_DATA) == 0);
+
+	gck_mechanism_unref (mech);
+}
+
+DEFINE_TEST(mech_ref_unref)
+{
+	GckMechanism *mech, *check;
+
+	mech = gck_mechanism_new (MECH_TYPE);
+	g_assert (mech);
+
+	check = gck_mechanism_ref (mech);
+	g_assert (check == mech);
+
+	gck_mechanism_unref (check);
+	gck_mechanism_unref (mech);
+}
+
+DEFINE_TEST(mech_unref_null)
+{
+	gck_mechanism_unref (NULL);
+}
diff --git a/gck/tests/test-gck-module.c b/gck/tests/test-gck-module.c
new file mode 100644
index 0000000..122d2d6
--- /dev/null
+++ b/gck/tests/test-gck-module.c
@@ -0,0 +1,159 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+
+static GckModule *module = NULL;
+
+DEFINE_SETUP(load_module)
+{
+	GError *err = NULL;
+
+	/* Successful load */
+	module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err);
+	SUCCESS_RES (module, err);
+}
+
+DEFINE_TEARDOWN(load_module)
+{
+	g_object_unref (module);
+}
+
+DEFINE_TEST(invalid_modules)
+{
+	GckModule *invalid;
+	GError *err = NULL;
+
+	/* Shouldn't be able to load modules */
+	invalid = gck_module_initialize ("blah-blah-non-existant", NULL, &err);
+	FAIL_RES (invalid, err);
+
+	/* Shouldn't be able to load any file successfully */
+	invalid = gck_module_initialize ("/usr/lib/libm.so", NULL, &err);
+	FAIL_RES (invalid, err);
+}
+
+DEFINE_TEST(module_equals_hash)
+{
+	GckModule *other;
+	GObject *obj;
+	guint hash;
+
+	hash = gck_module_hash (module);
+	g_assert (hash != 0);
+
+	g_assert (gck_module_equal (module, module));
+
+	other = gck_module_new (gck_module_get_functions (module));
+	obj = g_object_new (G_TYPE_OBJECT, NULL);
+
+	g_assert (gck_module_equal (module, other));
+
+	/* TODO: Could do with another test for inequality */
+	g_assert (!gck_module_equal (module, obj));
+
+	g_object_unref (other);
+	g_object_unref (obj);
+}
+
+DEFINE_TEST(module_props)
+{
+	gchar *path;
+
+	g_object_get (module, "path", &path, NULL);
+	g_assert (path != NULL && "no module-path");
+	g_assert (strcmp (".libs/libgck-test-module.so", path) == 0 && "module path wrong");
+	g_free (path);
+}
+
+DEFINE_TEST(module_info)
+{
+	GckModuleInfo *info;
+
+	info = gck_module_get_info (module);
+	g_assert (info != NULL && "no module info");
+
+	g_assert (info->pkcs11_version_major == CRYPTOKI_VERSION_MAJOR && "wrong major version");
+	g_assert (info->pkcs11_version_minor == CRYPTOKI_VERSION_MINOR && "wrong minor version");
+	g_assert (strcmp ("TEST MANUFACTURER", info->manufacturer_id) == 0);
+	g_assert (strcmp ("TEST LIBRARY", info->library_description) == 0);
+	g_assert (0 == info->flags);
+	g_assert (45 == info->library_version_major);
+	g_assert (145 == info->library_version_minor);
+
+	gck_module_info_free (info);
+}
+
+static int n_objects = 0;
+static GckObject *last_object = NULL;
+
+static gboolean
+for_each_object (GckObject *object, gpointer user_data)
+{
+	g_assert (GCK_IS_OBJECT (object));
+	g_assert_cmpstr ("blah", ==, user_data);
+	g_assert (user_data);
+
+	if (last_object)
+		g_object_unref (last_object);
+	last_object = g_object_ref (object);
+
+	++n_objects;
+
+	return TRUE;
+}
+
+static gboolean
+for_first_object (GckObject *object, gpointer user_data)
+{
+	g_assert (GCK_IS_OBJECT (object));
+	g_assert_cmpstr ("first", ==, user_data);
+	g_assert (user_data);
+
+	if (last_object)
+		g_object_unref (last_object);
+	last_object = g_object_ref (object);
+
+	++n_objects;
+
+	return FALSE;
+}
+
+DEFINE_TEST(module_enumerate)
+{
+	GckSession *session;
+	GckAttributes *attrs;
+	gboolean ret;
+
+	attrs = gck_attributes_new ();
+	ret = gck_module_enumerate_objects_full (module, attrs, NULL, for_first_object, "first", NULL);
+	g_assert (ret);
+	g_assert_cmpint (n_objects, ==, 1);
+	g_assert (GCK_IS_OBJECT (last_object));
+	gck_attributes_unref (attrs);
+
+	session = gck_object_get_session (last_object);
+	g_assert (GCK_IS_SESSION (session));
+	g_object_unref (session);
+
+	g_object_unref (last_object);
+	last_object = NULL;
+	n_objects = 0;
+
+	ret = gck_module_enumerate_objects (module, for_each_object, "blah",
+	                                     CKA_CLASS, GCK_ULONG, CKO_PRIVATE_KEY,
+	                                     GCK_INVALID);
+	g_assert (ret);
+	g_assert_cmpint (n_objects, ==, 2);
+	g_assert (GCK_IS_OBJECT (last_object));
+
+	session = gck_object_get_session (last_object);
+	g_assert (GCK_IS_SESSION (session));
+	g_object_unref (session);
+
+	g_object_unref (last_object);
+	last_object = NULL;
+	n_objects = 0;
+}
diff --git a/gck/tests/test-gck-object.c b/gck/tests/test-gck-object.c
new file mode 100644
index 0000000..2a63049
--- /dev/null
+++ b/gck/tests/test-gck-object.c
@@ -0,0 +1,463 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test-suite.h"
+
+#include <glib.h>
+
+#include "gck-test.h"
+
+static GckModule *module = NULL;
+static GckSlot *slot = NULL;
+static GckSession *session = NULL;
+static GckObject *object = NULL;
+
+DEFINE_SETUP(prep_object)
+{
+	GError *err = NULL;
+	GList *slots;
+
+	/* Successful load */
+	module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err);
+	SUCCESS_RES (module, err);
+
+	slots = gck_module_get_slots (module, TRUE);
+	g_assert (slots != NULL);
+
+	slot = GCK_SLOT (slots->data);
+	g_object_ref (slot);
+	gck_list_unref_free (slots);
+
+	session = gck_slot_open_session (slot, 0, &err);
+	SUCCESS_RES(session, err);
+
+	/* Our module always exports a token object with this */
+	object = gck_object_from_handle (slot, 2);
+	g_assert (object != NULL);
+}
+
+DEFINE_TEARDOWN(prep_object)
+{
+	g_object_unref (object);
+	g_object_unref (session);
+	g_object_unref (slot);
+	g_object_unref (module);
+}
+
+DEFINE_TEST(object_props)
+{
+	GckSlot *sl;
+	GckModule *mod;
+	CK_OBJECT_HANDLE handle;
+	g_object_get (object, "slot", &sl, "module", &mod, "handle", &handle, NULL);
+	g_assert (slot == sl);
+	g_object_unref (sl);
+	g_assert (module == mod);
+	g_object_unref (mod);
+	g_assert (handle == 2);
+}
+
+DEFINE_TEST(object_equals_hash)
+{
+	GckSlot *other_slot;
+	GckObject *other_object;
+	GObject *obj;
+	guint hash;
+
+	hash = gck_object_hash (object);
+	g_assert (hash != 0);
+
+	g_assert (gck_object_equal (object, object));
+
+	other_slot = g_object_new (GCK_TYPE_SLOT, "module", module, "handle", 5895, NULL);
+	other_object = gck_object_from_handle (other_slot, gck_object_get_handle (object));
+	g_assert (!gck_object_equal (object, other_object));
+	g_object_unref (other_slot);
+	g_object_unref (other_object);
+
+	obj = g_object_new (G_TYPE_OBJECT, NULL);
+	g_assert (!gck_object_equal (object, obj));
+	g_object_unref (obj);
+
+	other_object = gck_object_from_handle (slot, 383838);
+	g_assert (!gck_object_equal (object, other_object));
+	g_object_unref (other_object);
+
+	other_object = gck_object_from_handle (slot, gck_object_get_handle (object));
+	g_assert (gck_object_equal (object, other_object));
+	g_object_unref (other_object);
+}
+
+static void
+fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+	*((GAsyncResult**)user_data) = result;
+	g_object_ref (result);
+	testing_wait_stop ();
+}
+
+DEFINE_TEST(create_object)
+{
+	GAsyncResult *result = NULL;
+	GckAttributes *attrs;
+	GckObject *object;
+	CK_OBJECT_HANDLE last_handle;
+	GError *err = NULL;
+
+	/* Using simple */
+	object = gck_session_create_object (session, &err,
+	                                     CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                                     CKA_LABEL, GCK_STRING, "TEST LABEL",
+	                                     CKA_TOKEN, GCK_BOOLEAN, CK_FALSE,
+	                                     CKA_VALUE, 4UL, "BLAH",
+	                                     GCK_INVALID);
+	SUCCESS_RES (object, err);
+	g_assert (GCK_IS_OBJECT (object));
+
+	if (object) {
+		last_handle = gck_object_get_handle (object);
+		g_object_unref (object);
+	}
+
+	/* Using full */
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                              CKA_LABEL, GCK_STRING, "TEST LABEL",
+	                              CKA_TOKEN, GCK_BOOLEAN, CK_FALSE,
+	                              CKA_VALUE, 4UL, "BLAH",
+	                              GCK_INVALID);
+
+	object = gck_session_create_object_full (session, attrs, NULL, &err);
+	g_assert (GCK_IS_OBJECT (object));
+	SUCCESS_RES (object, err);
+
+	if (object) {
+		g_assert (last_handle != gck_object_get_handle (object));
+		last_handle = gck_object_get_handle (object);
+		g_object_unref (object);
+	}
+
+	/* Using async */
+	gck_session_create_object_async (session, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	object = gck_session_create_object_finish (session, result, &err);
+	g_object_unref (result);
+	SUCCESS_RES (object, err);
+	g_assert (GCK_IS_OBJECT (object));
+
+	if (object)
+		g_object_unref (object);
+	gck_attributes_unref (attrs);
+}
+
+DEFINE_TEST(destroy_object)
+{
+	GAsyncResult *result = NULL;
+	GckObject *object;
+	GError *err = NULL;
+	gboolean ret;
+
+	/* Using simple */
+	object = gck_session_create_object (session, &err,
+	                                     CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                                     CKA_LABEL, GCK_STRING, "TEST OBJECT",
+	                                     CKA_TOKEN, GCK_BOOLEAN, CK_TRUE,
+	                                     GCK_INVALID);
+	SUCCESS_RES (object, err);
+	g_assert (GCK_IS_OBJECT (object));
+
+	if (!object)
+		return;
+
+	ret = gck_object_destroy (object, &err);
+	SUCCESS_RES (ret, err);
+	g_object_unref (object);
+
+	/* Using full */
+	object = gck_session_create_object (session, &err,
+	                                     CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                                     CKA_LABEL, GCK_STRING, "TEST OBJECT",
+	                                     CKA_TOKEN, GCK_BOOLEAN, CK_TRUE,
+	                                     GCK_INVALID);
+	SUCCESS_RES (object, err);
+	g_assert (GCK_IS_OBJECT (object));
+
+	if (!object)
+		return;
+
+	ret = gck_object_destroy_full (object, NULL, &err);
+	SUCCESS_RES (ret, err);
+	g_object_unref (object);
+
+	/* Using async */
+	object = gck_session_create_object (session, &err,
+	                                     CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                                     CKA_LABEL, GCK_STRING, "TEST OBJECT",
+	                                     CKA_TOKEN, GCK_BOOLEAN, CK_TRUE,
+	                                     GCK_INVALID);
+	SUCCESS_RES (object, err);
+	g_assert (GCK_IS_OBJECT (object));
+
+	if (!object)
+		return;
+
+	/* Using async */
+	gck_object_destroy_async (object, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	ret = gck_object_destroy_finish (object, result, &err);
+	g_object_unref (result);
+	SUCCESS_RES (object, err);
+	g_object_unref (object);
+}
+
+DEFINE_TEST(get_attributes)
+{
+	GAsyncResult *result = NULL;
+	GckAttributes *attrs, *attrs_ret;
+	GError *err = NULL;
+	gulong klass;
+	gchar *value = NULL;
+
+	/* Simple */
+	attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID);
+	SUCCESS_RES (attrs, err);
+	if (attrs != NULL) {
+		g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == CKO_DATA);
+		g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "TEST LABEL") == 0);
+		g_free (value); value = NULL;
+	}
+	gck_attributes_unref (attrs);
+
+	/* Full */
+	attrs = gck_attributes_new_empty (CKA_CLASS, CKA_LABEL, GCK_INVALID);
+	attrs_ret = gck_object_get_full (object, attrs, NULL, &err);
+	SUCCESS_RES (attrs_ret, err);
+	if (attrs_ret != NULL) {
+		g_assert (attrs_ret == attrs);
+		g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == CKO_DATA);
+		g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "TEST LABEL") == 0);
+		g_free (value); value = NULL;
+	}
+	gck_attributes_unref (attrs);
+
+	/* Async */
+	attrs = gck_attributes_new_empty (CKA_CLASS, CKA_LABEL, GCK_INVALID);
+	gck_object_get_async (object, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	attrs_ret = gck_object_get_finish (object, result, &err);
+	g_object_unref (result);
+	SUCCESS_RES (attrs, err);
+	if (attrs != NULL) {
+		g_assert (attrs_ret == attrs);
+		g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == CKO_DATA);
+		g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "TEST LABEL") == 0);
+		g_free (value); value = NULL;
+	}
+	gck_attributes_unref (attrs);
+}
+
+DEFINE_TEST(get_data_attribute)
+{
+	GAsyncResult *result = NULL;
+	CK_OBJECT_CLASS_PTR klass;
+	gsize n_data;
+	GError *err = NULL;
+
+	/* Simple */
+	klass = gck_object_get_data (object, CKA_CLASS, &n_data, &err);
+	SUCCESS_RES (klass, err);
+	if (klass != NULL) {
+		g_assert (n_data == sizeof (CK_OBJECT_CLASS));
+		g_assert (*klass == CKO_DATA);
+		g_free (klass);
+	}
+
+	/* Full */
+	klass = gck_object_get_data_full (object, CKA_CLASS, NULL, NULL, &n_data, &err);
+	SUCCESS_RES (klass, err);
+	if (klass != NULL) {
+		g_assert (n_data == sizeof (CK_OBJECT_CLASS));
+		g_assert (*klass == CKO_DATA);
+		g_free (klass);
+	}
+
+	/* Async */
+	gck_object_get_data_async (object, CKA_CLASS, NULL, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	klass = gck_object_get_data_finish (object, result, &n_data, &err);
+	g_object_unref (result);
+	SUCCESS_RES (klass, err);
+	if (klass != NULL) {
+		g_assert (n_data == sizeof (CK_OBJECT_CLASS));
+		g_assert (*klass == CKO_DATA);
+		g_free (klass);
+	}
+
+}
+
+DEFINE_TEST(set_attributes)
+{
+	GAsyncResult *result = NULL;
+	GckAttributes *attrs, *templ;
+	GError *err = NULL;
+	gulong klass;
+	gchar *value = NULL;
+	gboolean ret;
+
+	/* Simple */
+	ret = gck_object_set (object, &err,
+	                       CKA_CLASS, GCK_ULONG, 5,
+	                       CKA_LABEL, GCK_STRING, "CHANGE ONE",
+	                       GCK_INVALID);
+	SUCCESS_RES (ret, err);
+	if (ret) {
+		attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID);
+		g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == 5);
+		g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "CHANGE ONE") == 0);
+		g_free (value); value = NULL;
+		gck_attributes_unref (attrs);
+	}
+
+	templ = gck_attributes_newv (CKA_CLASS, GCK_ULONG, 6,
+	                              CKA_LABEL, GCK_STRING, "CHANGE TWO",
+	                              GCK_INVALID);
+
+	/* Full */
+	ret = gck_object_set_full (object, templ, NULL, &err);
+	gck_attributes_unref (templ);
+	SUCCESS_RES (ret, err);
+	if (ret) {
+		attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID);
+		g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == 6);
+		g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "CHANGE TWO") == 0);
+		g_free (value); value = NULL;
+		gck_attributes_unref (attrs);
+	}
+
+	templ = gck_attributes_newv (CKA_CLASS, GCK_ULONG, 7,
+	                              CKA_LABEL, GCK_STRING, "CHANGE THREE",
+	                              GCK_INVALID);
+
+	/* Async */
+	gck_object_set_async (object, templ, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	ret = gck_object_set_finish (object, result, &err);
+	g_object_unref (result);
+	SUCCESS_RES (ret, err);
+	if (ret) {
+		attrs = gck_object_get (object, &err, CKA_CLASS, CKA_LABEL, GCK_INVALID);
+		g_assert (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass) && klass == 7);
+		g_assert (gck_attributes_find_string (attrs, CKA_LABEL, &value) && strcmp (value, "CHANGE THREE") == 0);
+		g_free (value); value = NULL;
+		gck_attributes_unref (attrs);
+	}
+}
+
+DEFINE_TEST(find_objects)
+{
+	GAsyncResult *result = NULL;
+	GckAttributes *templ;
+	GList *objects;
+	GckObject *testobj;
+	GError *err = NULL;
+
+	testobj = gck_session_create_object (session, &err,
+	                                      CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                                      CKA_LABEL, GCK_STRING, "UNIQUE LABEL",
+	                                      GCK_INVALID);
+	g_object_unref (testobj);
+
+	testobj = gck_session_create_object (session, &err,
+	                                      CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                                      CKA_LABEL, GCK_STRING, "OTHER LABEL",
+	                                      GCK_INVALID);
+	g_object_unref (testobj);
+
+	/* Simple, "TEST LABEL" */
+	objects = gck_session_find_objects (session, &err, CKA_LABEL, GCK_STRING, "UNIQUE LABEL", GCK_INVALID);
+	SUCCESS_RES (objects, err);
+	g_assert (g_list_length (objects) == 1);
+	gck_list_unref_free (objects);
+
+	/* Full, All */
+	templ = gck_attributes_new ();
+	objects = gck_session_find_objects_full (session, templ, NULL, &err);
+	SUCCESS_RES (objects, err);
+	g_assert (g_list_length (objects) > 1);
+	gck_list_unref_free (objects);
+
+	/* Async, None */
+	gck_attributes_add_string (templ, CKA_LABEL, "blah blah");
+	gck_session_find_objects_async (session, templ, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	objects = gck_session_find_objects_finish (session, result, &err);
+	g_object_unref (result);
+	g_assert (objects == NULL);
+	gck_list_unref_free (objects);
+}
+
+DEFINE_TEST(explicit_sessions)
+{
+	GckSession *sess;
+	GAsyncResult *result = NULL;
+	CK_OBJECT_CLASS_PTR klass;
+	GError *err = NULL;
+	gsize n_data;
+
+	/* Set an explicit session */
+	gck_object_set_session (object, session);
+	sess = gck_object_get_session (object);
+	g_assert (sess == session);
+	g_object_unref (sess);
+	g_object_get (object, "session", &sess, NULL);
+	g_assert (sess == session);
+	g_object_unref (sess);
+
+	/* Simple */
+	klass = gck_object_get_data (object, CKA_CLASS, &n_data, &err);
+	SUCCESS_RES (klass, err);
+	if (klass != NULL) {
+		g_assert (n_data == sizeof (CK_OBJECT_CLASS));
+		g_assert (*klass == CKO_DATA);
+		g_free (klass);
+	}
+
+	/* Async */
+	gck_object_get_data_async (object, CKA_CLASS, NULL, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	klass = gck_object_get_data_finish (object, result, &n_data, &err);
+	g_object_unref (result);
+	SUCCESS_RES (klass, err);
+	if (klass != NULL) {
+		g_assert (n_data == sizeof (CK_OBJECT_CLASS));
+		g_assert (*klass == CKO_DATA);
+		g_free (klass);
+	}
+
+	/* Set it to null and make sure taht works */
+	gck_object_set_session (object, NULL);
+	g_assert (gck_object_get_session (object) == NULL);
+	g_object_get (object, "session", &sess, NULL);
+	g_assert (sess == NULL);
+
+	/* Test property settor */
+	g_object_set (object, "session", session, NULL);
+	sess = gck_object_get_session (object);
+	g_assert (sess == session);
+	gck_object_set_session (object, NULL);
+	g_object_unref (sess);
+}
diff --git a/gck/tests/test-gck-session.c b/gck/tests/test-gck-session.c
new file mode 100644
index 0000000..ff98a0a
--- /dev/null
+++ b/gck/tests/test-gck-session.c
@@ -0,0 +1,323 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test-suite.h"
+
+#include <glib.h>
+
+#include "gck-test.h"
+
+static GckModule *module = NULL;
+static GckSlot *slot = NULL;
+static GckSession *session = NULL;
+
+DEFINE_SETUP(load_session)
+{
+	GError *err = NULL;
+	GList *slots;
+
+	/* Successful load */
+	module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err);
+	SUCCESS_RES (module, err);
+
+	slots = gck_module_get_slots (module, TRUE);
+	g_assert (slots != NULL);
+
+	slot = GCK_SLOT (slots->data);
+	g_object_ref (slot);
+	gck_list_unref_free (slots);
+
+	session = gck_slot_open_session (slot, 0, &err);
+	SUCCESS_RES(session, err);
+}
+
+DEFINE_TEARDOWN(load_session)
+{
+	g_object_unref (session);
+	g_object_unref (slot);
+	g_object_unref (module);
+}
+
+DEFINE_TEST(session_props)
+{
+	GckModule *mod;
+	GckSlot *sl;
+	gulong handle;
+
+	g_object_get (session, "module", &mod, "handle", &handle, "slot", &sl, NULL);
+	g_assert (mod == module);
+	g_assert (sl == slot);
+	g_object_unref (mod);
+	g_object_unref (sl);
+
+	g_assert (handle != 0);
+	g_assert (gck_session_get_handle (session) == handle);
+}
+
+DEFINE_TEST(session_info)
+{
+	GckSessionInfo *info;
+
+	info = gck_session_get_info (session);
+	g_assert (info != NULL && "no session info");
+
+	g_assert (info->slot_id == gck_slot_get_handle (slot));
+	g_assert ((info->flags & CKF_SERIAL_SESSION) == CKF_SERIAL_SESSION);
+	g_assert (info->device_error == 1414);
+	gck_session_info_free (info);
+}
+
+static void
+fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+	*((GAsyncResult**)user_data) = result;
+	g_object_ref (result);
+	testing_wait_stop ();
+}
+
+DEFINE_TEST(open_close_session)
+{
+	GckSession *sess;
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+
+	sess = gck_slot_open_session_full (slot, 0, NULL, NULL, NULL, &err);
+	SUCCESS_RES (sess, err);
+
+	g_object_unref (sess);
+
+	/* Test opening async */
+	gck_slot_open_session_async (slot, 0, NULL, NULL, NULL, fetch_async_result, &result);
+
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	sess = gck_slot_open_session_finish (slot, result, &err);
+	SUCCESS_RES (sess, err);
+
+	g_object_unref (result);
+	g_object_unref (sess);
+}
+
+DEFINE_TEST(open_reused)
+{
+	CK_OBJECT_HANDLE handle;
+	GckSession *sess, *sess2;
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+	gboolean value;
+
+	g_assert (gck_module_get_pool_sessions (module) == FALSE);
+	gck_module_set_pool_sessions (module, TRUE);
+	g_assert (gck_module_get_pool_sessions (module) == TRUE);
+	g_object_get (module, "pool-sessions", &value, NULL);
+	g_assert (value == TRUE);
+
+	sess = gck_slot_open_session (slot, 0, &err);
+	SUCCESS_RES (sess, err);
+	if (!sess) return;
+
+	/* Make note of the handle we saw */
+	handle = gck_session_get_handle (sess);
+	g_object_unref (sess);
+
+	/* Open again, and see if the same handle */
+	sess = gck_slot_open_session (slot, 0, &err);
+	SUCCESS_RES (sess, err);
+	if (!sess) return;
+	g_assert (handle == gck_session_get_handle (sess));
+	g_object_unref (sess);
+
+	/* Test opening async */
+	gck_slot_open_session_async (slot, 0, NULL, NULL, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	sess = gck_slot_open_session_finish (slot, result, &err);
+	SUCCESS_RES (sess, err);
+	if (!sess) return;
+	g_assert (handle == gck_session_get_handle (sess));
+	g_object_unref (result);
+	g_object_unref (sess);
+
+	/* Test opening with different flags, a different session should be returned */
+	sess = gck_slot_open_session (slot, CKF_RW_SESSION, &err);
+	SUCCESS_RES (sess, err);
+	if (!sess) return;
+	g_assert (handle != gck_session_get_handle (sess));
+
+	/* Now open a second session, with same flags, shouldn't return the same */
+	sess2 = gck_slot_open_session (slot, CKF_RW_SESSION, &err);
+	SUCCESS_RES (sess2, err);
+	if (!sess2) return;
+	g_assert (gck_session_get_handle (sess) != gck_session_get_handle (sess2));
+
+	g_object_set (module, "pool-sessions", FALSE, NULL);
+	g_assert (gck_module_get_pool_sessions (module) == FALSE);
+
+	g_object_unref (sess);
+	g_object_unref (sess2);
+}
+
+
+DEFINE_TEST(init_set_pin)
+{
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+	gboolean ret;
+
+	/* init pin */
+	ret = gck_session_init_pin (session, (guchar*)"booo", 4, &err);
+	SUCCESS_RES (ret, err);
+
+	/* set pin */
+	ret = gck_session_set_pin (session, (guchar*)"booo", 4, (guchar*)"tooo", 4, &err);
+	SUCCESS_RES (ret, err);
+
+	/* init pin async */
+	gck_session_init_pin_async (session, (guchar*)"booo", 4, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	ret = gck_session_init_pin_finish (session, result, &err);
+	SUCCESS_RES (ret, err);
+	g_object_unref (result);
+	result = NULL;
+
+	/* set pin async */
+	gck_session_set_pin_async (session, (guchar*)"booo", 4, (guchar*)"tooo", 4, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	ret = gck_session_set_pin_finish (session, result, &err);
+	SUCCESS_RES (ret, err);
+	g_object_unref (result);
+	result = NULL;
+}
+
+
+DEFINE_TEST(login_logout)
+{
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+	gboolean ret;
+
+	/* login/logout */
+	ret = gck_session_login (session, CKU_USER, (guchar*)"booo", 4, &err);
+	SUCCESS_RES (ret, err);
+
+	ret = gck_session_logout (session, &err);
+	SUCCESS_RES (ret, err);
+
+	/* login/logout full */
+	ret = gck_session_login_full (session, CKU_USER, (guchar*)"booo", 4, NULL, &err);
+	SUCCESS_RES (ret, err);
+
+	ret = gck_session_logout_full (session, NULL, &err);
+	SUCCESS_RES (ret, err);
+
+	/* login async */
+	gck_session_login_async (session, CKU_USER, (guchar*)"booo", 4, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	ret = gck_session_login_finish (session, result, &err);
+	SUCCESS_RES (ret, err);
+
+	g_object_unref (result);
+	result = NULL;
+
+	/* logout async */
+	gck_session_logout_async (session, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+
+	ret = gck_session_logout_finish (session, result, &err);
+	SUCCESS_RES (ret, err);
+
+	g_object_unref (result);
+	result = NULL;
+
+}
+
+static gboolean
+authenticate_token (GckModule *module, GckSlot *slot, gchar *label, gchar **password, gpointer unused)
+{
+	g_assert (unused == GUINT_TO_POINTER (35));
+	g_assert (password != NULL);
+	g_assert (*password == NULL);
+	g_assert (GCK_IS_MODULE (module));
+	g_assert (GCK_IS_SLOT (slot));
+
+	*password = g_strdup ("booo");
+	return TRUE;
+}
+
+DEFINE_TEST(auto_login)
+{
+	GckObject *object;
+	GckSession *new_session;
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+	GckAttributes *attrs;
+	gboolean ret;
+	gint value;
+
+	attrs = gck_attributes_newv (CKA_CLASS, GCK_ULONG, CKO_DATA,
+	                              CKA_LABEL, GCK_STRING, "TEST OBJECT",
+	                              CKA_PRIVATE, GCK_BOOLEAN, CK_TRUE,
+	                              GCK_INVALID);
+
+	/* Try to do something that requires a login */
+	object = gck_session_create_object_full (session, attrs, NULL, &err);
+	g_assert (!object);
+	g_assert (err && err->code == CKR_USER_NOT_LOGGED_IN);
+	g_clear_error (&err);
+
+	/* Setup for auto login */
+	g_assert (gck_module_get_auto_authenticate (module) == 0);
+	gck_module_set_auto_authenticate (module, TRUE);
+	g_assert (gck_module_get_auto_authenticate (module) == (GCK_AUTHENTICATE_TOKENS | GCK_AUTHENTICATE_OBJECTS));
+	g_object_get (module, "auto-authenticate", &value, NULL);
+	g_assert (value == (GCK_AUTHENTICATE_TOKENS | GCK_AUTHENTICATE_OBJECTS));
+
+	g_signal_connect (module, "authenticate-slot", G_CALLBACK (authenticate_token), GUINT_TO_POINTER (35));
+
+	/* Create a new session */
+	new_session = gck_slot_open_session (slot, CKF_RW_SESSION, &err);
+	SUCCESS_RES (new_session, err);
+	g_object_unref (new_session);
+
+	/* Try again to do something that requires a login */
+	object = gck_session_create_object_full (session, attrs, NULL, &err);
+	SUCCESS_RES (object, err);
+	g_object_unref (object);
+
+	/* We should now be logged in, try to log out */
+	ret = gck_session_logout (session, &err);
+	SUCCESS_RES (ret, err);
+
+	/* Now try the same thing, but asyncronously */
+	gck_slot_open_session_async (slot, CKF_RW_SESSION, NULL, NULL, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	new_session = gck_slot_open_session_finish (slot, result, &err);
+	SUCCESS_RES (new_session, err);
+	g_object_unref (result);
+	g_object_unref (new_session);
+
+	result = NULL;
+	gck_session_create_object_async (session, attrs, NULL, fetch_async_result, &result);
+	testing_wait_until (500);
+	g_assert (result != NULL);
+	object = gck_session_create_object_finish (session, result, &err);
+	SUCCESS_RES (object, err);
+	g_object_unref (result);
+	g_object_unref (object);
+
+	/* We should now be logged in, try to log out */
+	ret = gck_session_logout (session, &err);
+	SUCCESS_RES (ret, err);
+
+	g_object_set (module, "auto-authenticate", FALSE, NULL);
+	g_assert (gck_module_get_auto_authenticate (module) == FALSE);
+}
diff --git a/gck/tests/test-gck-slot.c b/gck/tests/test-gck-slot.c
new file mode 100644
index 0000000..6743508
--- /dev/null
+++ b/gck/tests/test-gck-slot.c
@@ -0,0 +1,147 @@
+
+#include <glib.h>
+#include <string.h>
+
+#include "test-suite.h"
+#include "gck-test.h"
+
+static GckModule *module = NULL;
+static GckSlot *slot = NULL;
+
+DEFINE_SETUP(load_slots)
+{
+	GError *err = NULL;
+	GList *slots;
+
+	/* Successful load */
+	module = gck_module_initialize (".libs/libgck-test-module.so", NULL, &err);
+	SUCCESS_RES (module, err);
+
+	slots = gck_module_get_slots (module, TRUE);
+	g_assert (slots != NULL);
+
+	slot = GCK_SLOT (slots->data);
+	g_object_ref (slot);
+	gck_list_unref_free (slots);
+
+}
+
+DEFINE_TEARDOWN(load_slots)
+{
+	g_object_unref (slot);
+	g_object_unref (module);
+}
+
+DEFINE_TEST(slot_info)
+{
+	GckSlotInfo *info;
+	GckTokenInfo *token;
+	GList *slots, *l;
+
+	slots = gck_module_get_slots (module, FALSE);
+	g_assert (2 == g_list_length (slots) && "wrong number of slots returned");
+	g_assert (GCK_IS_SLOT (slots->data) && "missing slot one");
+	g_assert (GCK_IS_SLOT (slots->next->data) && "missing slot two");
+
+	for (l = slots; l; l = g_list_next (l)) {
+		info = gck_slot_get_info (GCK_SLOT (l->data));
+		g_assert (info != NULL && "no slot info");
+
+		g_assert (strcmp("TEST MANUFACTURER", info->manufacturer_id) == 0);
+		g_assert (strcmp("TEST SLOT", info->slot_description) == 0);
+		g_assert (55 == info->hardware_version_major);
+		g_assert (155 == info->hardware_version_minor);
+		g_assert (65 == info->firmware_version_major);
+		g_assert (165 == info->firmware_version_minor);
+
+		if (info->flags & CKF_TOKEN_PRESENT) {
+			token = gck_slot_get_token_info (slot);
+			g_assert (token != NULL && "no token info");
+
+			g_assert (strcmp ("TEST MANUFACTURER", token->manufacturer_id) == 0);
+			g_assert (strcmp ("TEST LABEL", token->label) == 0);
+			g_assert (strcmp ("TEST MODEL", token->model) == 0);
+			g_assert (strcmp ("TEST SERIAL", token->serial_number) == 0);
+			g_assert (1 == token->max_session_count);
+			g_assert (2 == token->session_count);
+			g_assert (3 == token->max_rw_session_count);
+			g_assert (4 == token->rw_session_count);
+			g_assert (5 == token->max_pin_len);
+			g_assert (6 == token->min_pin_len);
+			g_assert (7 == token->total_public_memory);
+			g_assert (8 == token->free_public_memory);
+			g_assert (9 == token->total_private_memory);
+			g_assert (10 == token->free_private_memory);
+			g_assert (75 == token->hardware_version_major);
+			g_assert (175 == token->hardware_version_minor);
+			g_assert (85 == token->firmware_version_major);
+			g_assert (185 == token->firmware_version_minor);
+			g_assert (927623999 == token->utc_time);
+
+			gck_token_info_free (token);
+		}
+
+		gck_slot_info_free (info);
+	}
+
+	gck_list_unref_free (slots);
+}
+
+DEFINE_TEST(slot_props)
+{
+	GckModule *mod;
+	CK_SLOT_ID slot_id;
+
+	g_object_get (slot, "module", &mod, "handle", &slot_id, NULL);
+	g_assert (mod == module);
+	g_assert (slot_id == 52);
+
+	g_object_unref (mod);
+}
+
+DEFINE_TEST(slot_equals_hash)
+{
+	GckModule *other_mod;
+	GckSlot *other_slot;
+	GObject *obj;
+	guint hash;
+
+	hash = gck_slot_hash (slot);
+	g_assert (hash != 0);
+
+	g_assert (gck_slot_equal (slot, slot));
+
+	other_mod = gck_module_new (gck_module_get_functions (module));
+	other_slot = g_object_new (GCK_TYPE_SLOT, "module", other_mod, "handle", gck_slot_get_handle (slot), NULL);
+	g_assert (gck_slot_equal (slot, other_slot));
+	g_object_unref (other_mod);
+	g_object_unref (other_slot);
+
+	obj = g_object_new (G_TYPE_OBJECT, NULL);
+	g_assert (!gck_slot_equal (slot, obj));
+	g_object_unref (obj);
+
+	other_slot = g_object_new (GCK_TYPE_SLOT, "module", module, "handle", 8909, NULL);
+	g_assert (!gck_slot_equal (slot, obj));
+	g_object_unref (other_slot);
+}
+
+DEFINE_TEST(slot_mechanisms)
+{
+	GckMechanisms *mechs;
+	GckMechanismInfo *info;
+	guint i;
+
+	mechs = gck_slot_get_mechanisms (slot);
+	g_assert (2 == gck_mechanisms_length (mechs) && "wrong number of mech types returned");
+
+	for (i = 0; i < gck_mechanisms_length (mechs); ++i) {
+
+		info = gck_slot_get_mechanism_info (slot, gck_mechanisms_at (mechs, i));
+		g_assert (info != NULL && "no mech info returned");
+
+		gck_mechanism_info_free (info);
+	}
+
+	gck_mechanisms_free (mechs);
+}



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