summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2010-07-29 16:34:07 +0200
committerStef Walter <stef@memberwebs.com>2010-07-29 16:34:07 +0200
commit8d80ac517dea7f35b5bb7b72fe425681d4ed5601 (patch)
tree6e6c0a6a9ca80f14c9ce5a83de6bbb8cc8a67109
parent68cb40cc11a8188cd4f8e608ab882f19e6d51329 (diff)
downloadgnome-keyring-8d80ac517dea7f35b5bb7b72fe425681d4ed5601.tar.gz
[gck] In preparation for public release, rename library.
* Next steps will include cleaning up the API making it ready for gobject introspection etc..
-rw-r--r--Makefile.am1
-rw-r--r--configure.in11
-rw-r--r--gck/.gitignore2
-rw-r--r--gck/Makefile.am66
-rw-r--r--gck/gck-attributes.c1428
-rw-r--r--gck/gck-call.c541
-rw-r--r--gck/gck-marshal.list3
-rw-r--r--gck/gck-misc.c437
-rw-r--r--gck/gck-module.c1228
-rw-r--r--gck/gck-object.c1615
-rw-r--r--gck/gck-private.h162
-rw-r--r--gck/gck-session.c2886
-rw-r--r--gck/gck-slot.c1061
-rw-r--r--gck/gck.h1297
-rw-r--r--gck/gck.pc.in14
-rw-r--r--gck/pkcs11.h30
-rw-r--r--gck/tests/Makefile.am42
-rw-r--r--gck/tests/gck-test-module.c1702
-rw-r--r--gck/tests/gck-test.h46
-rw-r--r--gck/tests/test-gck-attributes.c528
-rw-r--r--gck/tests/test-gck-crypto.c595
-rw-r--r--gck/tests/test-gck-mechanism.c60
-rw-r--r--gck/tests/test-gck-module.c159
-rw-r--r--gck/tests/test-gck-object.c463
-rw-r--r--gck/tests/test-gck-session.c323
-rw-r--r--gck/tests/test-gck-slot.c147
26 files changed, 14847 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index ca70b917..88c56cea 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 e3a14cac..74739cd2 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 00000000..4fbdec3a
--- /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 00000000..0173680c
--- /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 00000000..30250e89
--- /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 00000000..939d81bb
--- /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 00000000..d9553afa
--- /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 00000000..ae8c66d5
--- /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 00000000..096a0442
--- /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 00000000..bc3e8ade
--- /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 00000000..f30f21c3
--- /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 00000000..dc34f2f4
--- /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 00000000..e4092bf0
--- /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 00000000..5e74b952
--- /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 00000000..b0a812d4
--- /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 00000000..c0981c83
--- /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 00000000..482f6b2e
--- /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 00000000..e6ad4dfe
--- /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 00000000..078c962e
--- /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 00000000..24e8ee32
--- /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 00000000..f03b7e7c
--- /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 00000000..6667103e
--- /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 00000000..122d2d6c
--- /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 00000000..2a630495
--- /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 00000000..ff98a0a5
--- /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 00000000..6743508a
--- /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);
+}