gnome-keyring r1459 - in trunk: . daemon daemon/keyrings daemon/keyrings/tests daemon/pkcs11 pkcs11 pkcs11/gck pkcs11/gck/tests pkcs11/gck/tests/test-data pkcs11/roots-store pkcs11/ssh-store pkcs11/ssh-store/tests pkcs11/tests pkcs11/user-store tests



Author: nnielsen
Date: Sat Jan 17 20:17:52 2009
New Revision: 1459
URL: http://svn.gnome.org/viewvc/gnome-keyring?rev=1459&view=rev

Log:
	* daemon/keyrings/gkr-keyring-login.c:
	* daemon/keyrings/gkr-keyring-login.h: 
	* daemon/keyrings/tests/unit-test-keyring-login.c: Add functions for accessing 
	the 'master' login password.

	* daemon/pkcs11/gkr-pkcs11-auth.c:
	* daemon/pkcs11/gkr-pkcs11-auth.h:
	* daemon/pkcs11/gkr-pkcs11-auth-ep.c: Support automaticalcly initializing a token
	when it doesn't have CKF_USER_PIN_NOT_INITIALIZED

	* daemon/Makefile.am:
	* daemon/pkcs11/gkr-pkcs11-daemon.c: 	
	* pkcs11/Makefile.am:
	* pkcs11/gck/gck-certificate.c:
	* pkcs11/gck/gck-certificate.h:
	* pkcs11/gck/gck-crypto.c:
	* pkcs11/gck/gck-crypto.h:
	* pkcs11/gck/gck-data-asn1.c:
	* pkcs11/gck/gck-data-der.c:
	* pkcs11/gck/gck-data-der.h:
	* pkcs11/gck/gck-data-file.c: (added)
	* pkcs11/gck/gck-data-file.h: (added)
	* pkcs11/gck/gck-data-openssl.c:
	* pkcs11/gck/gck-file-store.c: (removed)
	* pkcs11/gck/gck-file-store.h: (removed)
	* pkcs11/gck/gck-login.c: (added)
	* pkcs11/gck/gck-login.h: (added)
	* pkcs11/gck/gck-marshal.list:
	* pkcs11/gck/gck-module.c:
	* pkcs11/gck/gck-module.h:
	* pkcs11/gck/gck-module-ep.h:
	* pkcs11/gck/gck-serializable.c:
	* pkcs11/gck/gck-serializable.h: 
	* pkcs11/gck/gck-session.c:
	* pkcs11/gck/gck-session.h:
	* pkcs11/gck/gck-store.h:
	* pkcs11/gck/gck-transaction.c:
	* pkcs11/gck/gck-transaction.h:
	* pkcs11/gck/gck-types.h:
	* pkcs11/gck/gck-util.c:
	* pkcs11/gck/gck-util.h:
	* pkcs11/gck/Makefile.am:
	* pkcs11/gck/tests/Makefile.am:
	* pkcs11/gck/tests/unit-test-crypto.c:
	* pkcs11/gck/tests/unit-test-data-asn1.c:
	* pkcs11/gck/tests/unit-test-data-der.c:
	* pkcs11/gck/tests/unit-test-data-file.c: (added)
	* pkcs11/gck/tests/unit-test-data-openssl.c: 
	* pkcs11/gck/tests/unit-test-file-store.c: (removed)
	* pkcs11/gck/tests/unit-test-login.c: (added)
	* pkcs11/gck/tests/unit-test-transaction.c:
	* pkcs11/gck/tests/unit-test-util.c: (added)
	* pkcs11/gck/tests/test-data/data-file-*: (added)
	* pkcs11/gck/tests/test-data/der-pkcs8-*: (added)
	* pkcs11/gck/tests/test-data/test-file-store.store: (removed)
	* pkcs11/roots-store/gck-roots-module.c:
	* pkcs11/ssh-store/gck-ssh-module.c:
	* pkcs11/ssh-store/tests/unit-test-ssh-openssh.c:
	* pkcs11/tests: (removed)
	* pkcs11/user-store/gck-user-module.c: (added)
	* pkcs11/user-store/gck-user-module.h: (added)
	* pkcs11/user-store/gck-user-private-key.c: (added)
	* pkcs11/user-store/gck-user-private-key.h: (added)
	* pkcs11/user-store/gck-user-public-key.c: (added)
	* pkcs11/user-store/gck-user-public-key.h: (added)
	* pkcs11/user-store/gck-user-standalone.c: (added)
	* pkcs11/user-store/gck-user-storage.c: (added)
	* pkcs11/user-store/gck-user-storage.h: (added)
	* pkcs11/user-store/gck-user-store.h: (added)
	* pkcs11/user-store/Makefile.am: (added)
	* tests/gtest-helpers.c:
	* tests/gtest-helpers.h: Add the user-store module and necessary code 
	changes to support it.


Added:
   trunk/pkcs11/gck/gck-data-file.c
   trunk/pkcs11/gck/gck-data-file.h
   trunk/pkcs11/gck/gck-login.c
   trunk/pkcs11/gck/gck-login.h
   trunk/pkcs11/gck/gck-serializable.c
   trunk/pkcs11/gck/gck-serializable.h
   trunk/pkcs11/gck/tests/test-data/data-file-private.store   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/data-file-public.store   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-MD5-DES.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-DES.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-dsa.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-encrypted-pkcs5.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des3.key   (contents, props changed)
   trunk/pkcs11/gck/tests/test-data/der-pkcs8.key   (contents, props changed)
   trunk/pkcs11/gck/tests/unit-test-data-file.c   (contents, props changed)
      - copied, changed from r1452, /trunk/pkcs11/gck/tests/unit-test-file-store.c
   trunk/pkcs11/gck/tests/unit-test-login.c
   trunk/pkcs11/gck/tests/unit-test-util.c
   trunk/pkcs11/user-store/   (props changed)
   trunk/pkcs11/user-store/Makefile.am
   trunk/pkcs11/user-store/gck-user-module.c
   trunk/pkcs11/user-store/gck-user-module.h
   trunk/pkcs11/user-store/gck-user-private-key.c
   trunk/pkcs11/user-store/gck-user-private-key.h
   trunk/pkcs11/user-store/gck-user-public-key.c
   trunk/pkcs11/user-store/gck-user-public-key.h
   trunk/pkcs11/user-store/gck-user-standalone.c
   trunk/pkcs11/user-store/gck-user-storage.c
   trunk/pkcs11/user-store/gck-user-storage.h
   trunk/pkcs11/user-store/gck-user-store.h
Removed:
   trunk/pkcs11/gck/gck-file-store.c
   trunk/pkcs11/gck/gck-file-store.h
   trunk/pkcs11/gck/tests/test-data/test-file-store.store
   trunk/pkcs11/gck/tests/unit-test-file-store.c
   trunk/pkcs11/tests/
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/daemon/Makefile.am
   trunk/daemon/keyrings/gkr-keyring-login.c
   trunk/daemon/keyrings/gkr-keyring-login.h
   trunk/daemon/keyrings/tests/unit-test-keyring-login.c
   trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c
   trunk/daemon/pkcs11/gkr-pkcs11-auth.c
   trunk/daemon/pkcs11/gkr-pkcs11-auth.h
   trunk/daemon/pkcs11/gkr-pkcs11-daemon.c
   trunk/pkcs11/Makefile.am
   trunk/pkcs11/gck/Makefile.am
   trunk/pkcs11/gck/gck-certificate.c
   trunk/pkcs11/gck/gck-certificate.h
   trunk/pkcs11/gck/gck-crypto.c
   trunk/pkcs11/gck/gck-crypto.h
   trunk/pkcs11/gck/gck-data-asn1.c
   trunk/pkcs11/gck/gck-data-der.c
   trunk/pkcs11/gck/gck-data-der.h
   trunk/pkcs11/gck/gck-data-openssl.c
   trunk/pkcs11/gck/gck-marshal.list
   trunk/pkcs11/gck/gck-module-ep.h
   trunk/pkcs11/gck/gck-module.c
   trunk/pkcs11/gck/gck-module.h
   trunk/pkcs11/gck/gck-session.c
   trunk/pkcs11/gck/gck-session.h
   trunk/pkcs11/gck/gck-store.h
   trunk/pkcs11/gck/gck-transaction.c
   trunk/pkcs11/gck/gck-transaction.h
   trunk/pkcs11/gck/gck-types.h
   trunk/pkcs11/gck/gck-util.c
   trunk/pkcs11/gck/gck-util.h
   trunk/pkcs11/gck/tests/Makefile.am
   trunk/pkcs11/gck/tests/unit-test-crypto.c
   trunk/pkcs11/gck/tests/unit-test-data-asn1.c
   trunk/pkcs11/gck/tests/unit-test-data-der.c
   trunk/pkcs11/gck/tests/unit-test-data-openssl.c
   trunk/pkcs11/gck/tests/unit-test-transaction.c
   trunk/pkcs11/roots-store/gck-roots-module.c
   trunk/pkcs11/ssh-store/gck-ssh-module.c
   trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c
   trunk/tests/gtest-helpers.c
   trunk/tests/gtest-helpers.h

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Sat Jan 17 20:17:52 2009
@@ -514,7 +514,8 @@
 pkcs11/ssh-agent/Makefile
 pkcs11/ssh-store/Makefile
 pkcs11/ssh-store/tests/Makefile
-pkcs11/tests/Makefile
+pkcs11/user-store/Makefile
+pkcs11/user-store/tests/Makefile
 po/Makefile.in
 tests/Makefile
 tool/Makefile

Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am	(original)
+++ trunk/daemon/Makefile.am	Sat Jan 17 20:17:52 2009
@@ -48,6 +48,7 @@
 	$(top_builddir)/pkcs11/rpc-layer/libgck-rpc-layer.la \
 	$(top_builddir)/pkcs11/ssh-agent/libgck-ssh-agent.la \
 	$(top_builddir)/pkcs11/ssh-store/libgck-ssh-store.la \
+	$(top_builddir)/pkcs11/user-store/libgck-user-store.la \
 	$(top_builddir)/pkcs11/gck/libgck.la \
 	$(top_builddir)/common/libgkr-common.la \
 	$(top_builddir)/gp11/libgp11.la \

Modified: trunk/daemon/keyrings/gkr-keyring-login.c
==============================================================================
--- trunk/daemon/keyrings/gkr-keyring-login.c	(original)
+++ trunk/daemon/keyrings/gkr-keyring-login.c	Sat Jan 17 20:17:52 2009
@@ -215,6 +215,21 @@
 	return attributes;
 }
 
+const gchar*
+gkr_keyring_login_master (void)
+{
+	GkrKeyring *login;
+	
+	login = gkr_keyrings_get_login ();
+	if (!login || login->locked)
+		return NULL;
+	
+	if (gkr_keyring_is_insecure (login))
+		return NULL;
+	
+	return login->password;
+}
+
 void
 gkr_keyring_login_attach_secret (GnomeKeyringItemType type, const gchar *display_name, 
                                  const gchar *secret, ...)

Modified: trunk/daemon/keyrings/gkr-keyring-login.h
==============================================================================
--- trunk/daemon/keyrings/gkr-keyring-login.h	(original)
+++ trunk/daemon/keyrings/gkr-keyring-login.h	Sat Jan 17 20:17:52 2009
@@ -34,6 +34,8 @@
 
 void            gkr_keyring_login_lock           (void);
 
+const gchar*    gkr_keyring_login_master         (void);
+
 void            gkr_keyring_login_attach_secret  (GnomeKeyringItemType type, 
                                                   const gchar *display_name, 
                                                   const gchar *secret,

Modified: trunk/daemon/keyrings/tests/unit-test-keyring-login.c
==============================================================================
--- trunk/daemon/keyrings/tests/unit-test-keyring-login.c	(original)
+++ trunk/daemon/keyrings/tests/unit-test-keyring-login.c	Sat Jan 17 20:17:52 2009
@@ -99,6 +99,13 @@
 	          login == gkr_keyrings_get_login());
 }
 
+void unit_test_keyrings_login_master (CuTest *cu)
+{
+	const gchar *master = gkr_keyring_login_master();
+	CuAssert (cu, "no master password in login keyring", master != NULL);
+	CuAssert (cu, "wrong master password in login keyring", strcmp (master, "blah") == 0);
+}
+
 void unit_test_keyrings_login_secrets (CuTest* cu)
 {
 	const gchar *password;

Modified: trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c	(original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c	Sat Jan 17 20:17:52 2009
@@ -136,6 +136,53 @@
 	return info;
 }
 
+static CK_RV 
+perform_set_user_pin (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG n_old_pin,
+                      CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin, gboolean also_login)
+{
+	CK_SESSION_INFO session_info;
+	CK_TOKEN_INFO token_info;
+	gboolean auth = FALSE;
+	CK_RV rv, login_rv;
+	
+	/* Dig up the information we'll need, and don't prompt if protected auth path */
+	if ((pkcs11_lower->C_GetSessionInfo) (handle, &session_info) == CKR_OK &&
+	    (pkcs11_lower->C_GetTokenInfo) (session_info.slotID, &token_info) == CKR_OK && 
+	    !(token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { 
+
+		gkr_async_end_concurrent ();
+		
+			if (!(token_info.flags & CKF_USER_PIN_INITIALIZED))
+				auth = gkr_pkcs11_auth_init_user_prompt (handle, &token_info, &new_pin, &n_new_pin);
+			/* TODO: Prompt for other 'change password' case */
+
+		gkr_async_begin_concurrent ();
+	}
+
+	rv = (pkcs11_lower->C_SetPIN) (handle, old_pin, n_old_pin, new_pin, n_new_pin);
+	
+	/* If requested we can also login, this prevents two prompts */
+	login_rv = CKR_OK;
+	if (rv == CKR_OK) {
+		login_rv = (pkcs11_lower->C_Login) (handle, CKU_USER, new_pin, n_new_pin);
+	}
+	
+	if (auth) {
+		gkr_async_end_concurrent ();
+		
+			if (!(token_info.flags & CKF_USER_PIN_INITIALIZED))
+				gkr_pkcs11_auth_init_user_done (handle, &token_info, &new_pin, &n_new_pin, rv);
+			/* TODO: Done for other case */
+			
+		gkr_async_begin_concurrent ();
+	}
+	
+	if (login_rv != CKR_OK)
+		rv = login_rv;
+	
+	return rv;
+}
+
 /* --------------------------------------------------------------------------------------
  * PKCS#11 ENTRY POINTS
  */
@@ -358,16 +405,10 @@
 }
 
 static CK_RV
-auth_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_pin_len, 
-               CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
+auth_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG n_old_pin, 
+               CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
 {
-	/* 
-	 * TODO: Need to implement this properly.
-	 * 
-	 * Prompt the user for old and new passwords if 
-	 * CKF_PROTECTED_AUTHENTICATION path.
-	 */
-	return (pkcs11_lower->C_SetPIN) (handle, old_pin, old_pin_len, new_pin, new_pin_len);
+	return perform_set_user_pin (handle, old_pin, n_old_pin, new_pin, n_new_pin, FALSE);
 }
 
 static CK_RV
@@ -395,9 +436,22 @@
 
 	/* Try the login first, this allows NULL logins to be tried */
 	rv = (pkcs11_lower->C_Login) (handle, user_type, pin, pin_len);
+	
+	if (rv == CKR_USER_PIN_NOT_INITIALIZED) {
+
+		/* 
+		 * Try and initialize the token login, gnome-keyring modules allow 
+		 * this to be called on CKU_USER if no user pin has yet been set.
+		 */
+		
+		if (user_type == CKU_USER) 
+			return perform_set_user_pin (handle, NULL, 0, NULL, 0, TRUE);
+		
+		return rv;
+	}
 
 	/* See if we can help the login to work */
-	if (pin != NULL || rv != CKR_PIN_INCORRECT)
+	if (rv != CKR_PIN_INCORRECT)
 		return rv;
 
 	/* Dig up the information we'll need */

Modified: trunk/daemon/pkcs11/gkr-pkcs11-auth.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-auth.c	(original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-auth.c	Sat Jan 17 20:17:52 2009
@@ -418,23 +418,15 @@
 	return ret;
 }
 
-void 
-gkr_pkcs11_auth_login_user_done (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
-                                 CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len, CK_RV rv)
+static void
+clear_user_login (CK_TOKEN_INFO *info)
 {
 	gchar *manufacturer;
 	gchar *serial;
-
-	g_assert (pin);
-	g_assert (pin_len);
 	
-	switch (rv) {
-	case CKR_PIN_INCORRECT:
-	case CKR_PIN_EXPIRED:
-	case CKR_PIN_INVALID:
-	case CKR_PIN_LEN_RANGE:
-	case CKR_PIN_LOCKED:
-		
+	g_assert (info);
+	
+	if (gkr_keyring_login_is_usable ()) {
 		/* 
 		 * The manufacturer and serial number together uniquely identify token 
 		 * They're stored with space padded in the token info structure.
@@ -447,13 +439,29 @@
 		g_strchomp (serial);
 
 		gkr_keyring_login_remove_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD,
-		                                 "manufacturer", manufacturer, 
-		                                 "serial-number", serial, 
-		                                 NULL);
+						 "manufacturer", manufacturer, 
+						 "serial-number", serial, 
+						 NULL);
 		
 		g_free (manufacturer);
 		g_free (serial);
-		
+	}
+}
+
+void 
+gkr_pkcs11_auth_login_user_done (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
+                                 CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len, CK_RV rv)
+{
+	g_assert (pin);
+	g_assert (pin_len);
+	
+	switch (rv) {
+	case CKR_PIN_INCORRECT:
+	case CKR_PIN_EXPIRED:
+	case CKR_PIN_INVALID:
+	case CKR_PIN_LEN_RANGE:
+	case CKR_PIN_LOCKED:
+		clear_user_login (info);
 		break;
 	}
 	
@@ -463,6 +471,120 @@
 	*pin_len = 0;
 }
 
+gboolean
+gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
+                                  CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len)
+{
+	GkrAskRequest *ask;
+	gchar *label;
+	gchar *secondary;
+	gchar *manufacturer;
+	gchar *serial;
+	const gchar *password;
+	gboolean ret = TRUE;
+	guint flags;
+	
+	g_assert (info);
+	g_assert (pin);
+	g_assert (pin_len);
+	
+	/* 
+	 * The manufacturer and serial number together uniquely identify token 
+	 * They're stored with space padded in the token info structure.
+	 */
+	
+	manufacturer = g_strndup ((gchar*)info->manufacturerID, sizeof (info->manufacturerID));
+	g_strchomp (manufacturer);
+
+	serial = g_strndup ((gchar*)info->serialNumber, sizeof (info->serialNumber));
+	g_strchomp (serial);
+
+	label = g_strndup ((gchar*)info->label, sizeof (info->label));
+	g_strchomp (label);
+
+	/* We try to use the login keyring password if available */
+	password = gkr_keyring_login_master ();
+	if (password != NULL) {
+		password_to_pin (password, pin, pin_len);
+		
+		/* Save this away in case the main password changes without us being aware */
+		if (gkr_keyring_login_is_usable ())
+			gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD, 
+			                                 label, password,
+			                                 "manufacturer", manufacturer, 
+			                                 "serial-number", serial,
+			                                 NULL);
+		
+		g_free (manufacturer);
+		g_free (serial);
+		g_free (label);
+		return TRUE;
+	}
+
+	/* Otherwise we have to prompt for it */
+	
+	/* Build up the prompt */
+	flags = GKR_ASK_REQUEST_NEW_PASSWORD;
+	ask = gkr_ask_request_new (_("New Password Required"), 
+	                           _("New password required for secure storage"), flags);
+
+	secondary = g_strdup_printf (_("In order to prepare '%s' for storage of certificates or keys, a password is required"), label);
+	gkr_ask_request_set_secondary (ask, secondary);
+	g_free (secondary);
+
+	if (gkr_keyring_login_is_usable ())
+		gkr_ask_request_set_check_option (ask, _("Automatically unlock secure storage when I log in."));
+
+	/* Prompt the user */
+	gkr_ask_daemon_process (ask);
+
+	/* If the user denied ... */
+	if (ask->response == GKR_ASK_RESPONSE_DENY) {
+		ret = FALSE;
+		
+	/* User cancelled or failure */
+	} else if (ask->response < GKR_ASK_RESPONSE_ALLOW) {
+		ret = FALSE;
+			
+	/* Successful response */
+	} else {
+		password_to_pin (ask->typed_password, pin, pin_len);
+		
+		if (ask->checked) {
+			gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD, 
+			                                 label, ask->typed_password,
+			                                 "manufacturer", manufacturer, 
+			                                 "serial-number", serial,
+			                                 NULL);
+		}
+		
+		ret = TRUE;
+	}
+	
+	g_free (manufacturer);
+	g_free (serial);
+	g_free (label);
+	g_object_unref (ask);
+	
+	return ret;
+}
+
+void
+gkr_pkcs11_auth_init_user_done (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *token_info, 
+                                CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len, CK_RV rv)
+{
+	g_assert (pin);
+	g_assert (pin_len);
+	
+	if (rv != CKR_OK)
+		clear_user_login (token_info);
+	
+	gkr_secure_strfree ((gchar*)*pin);
+	
+	*pin = NULL;
+	*pin_len = 0;
+}
+
 static void
 free_slot_data (SlotData *slot)
 {

Modified: trunk/daemon/pkcs11/gkr-pkcs11-auth.h
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-auth.h	(original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-auth.h	Sat Jan 17 20:17:52 2009
@@ -64,6 +64,17 @@
                                                                           CK_ULONG *pin_len,
                                                                           CK_RV rv);
 
+gboolean                        gkr_pkcs11_auth_init_user_prompt         (CK_SESSION_HANDLE handle, 
+                                                                          CK_TOKEN_INFO *token_info, 
+                                                                          CK_UTF8CHAR_PTR *pin, 
+                                                                          CK_ULONG *pin_len);
+
+void                            gkr_pkcs11_auth_init_user_done           (CK_SESSION_HANDLE handle, 
+                                                                          CK_TOKEN_INFO *token_info, 
+                                                                          CK_UTF8CHAR_PTR *pin, 
+                                                                          CK_ULONG *pin_len,
+                                                                          CK_RV rv);
+
 void                            gkr_pkcs11_auth_initialized              (void);
 
 void                            gkr_pkcs11_auth_session_opened           (CK_SESSION_HANDLE handle,

Modified: trunk/daemon/pkcs11/gkr-pkcs11-daemon.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-daemon.c	(original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-daemon.c	Sat Jan 17 20:17:52 2009
@@ -29,6 +29,7 @@
 #include "pkcs11/rpc-layer/gck-rpc-layer.h"
 #include "pkcs11/ssh-agent/gck-ssh-agent.h"
 #include "pkcs11/ssh-store/gck-ssh-store.h"
+#include "pkcs11/user-store/gck-user-store.h"
 
 #include "common/gkr-async.h"
 #include "common/gkr-daemon-util.h"
@@ -74,22 +75,28 @@
 	CK_FUNCTION_LIST_PTR plex_layer;
 	CK_FUNCTION_LIST_PTR roots_store; 
 	CK_FUNCTION_LIST_PTR ssh_store;
+	CK_FUNCTION_LIST_PTR user_store;
 	CK_RV rv;
 
 	/* Now initialize them all */
 	gkr_async_begin_concurrent ();
 
-		/* Connect SSH storage */
+		/* SSH storage */
 		ssh_store = gck_ssh_store_get_functions ();
 		
-		/* Connect Root certificates */
-		roots_store = gck_roots_store_get_functions (); 
+		/* Root certificates */
+		roots_store = gck_roots_store_get_functions ();
+		
+		/* User certificates */
+		user_store = gck_user_store_get_functions ();
 		
 		/* Add all of those into the multiplexing layer */
 		gck_plex_layer_add_module (ssh_store);
 #ifdef ROOT_CERTIFICATES
 		gck_plex_layer_add_module (roots_store);
 #endif
+		gck_plex_layer_add_module (user_store);
+		
 		plex_layer = gck_plex_layer_get_functions (); 
 		
 		/* The auth component is the top component */

Modified: trunk/pkcs11/Makefile.am
==============================================================================
--- trunk/pkcs11/Makefile.am	(original)
+++ trunk/pkcs11/Makefile.am	Sat Jan 17 20:17:52 2009
@@ -9,12 +9,6 @@
 EXTRA_DIST = \
 	pkcs11.h
 
-if WITH_TESTS
-TESTS_DIR = tests
-else
-TESTS_DIR = 
-endif
-
 SUBDIRS = . \
 	gck \
 	roots-store \
@@ -22,5 +16,4 @@
 	plex-layer \
 	ssh-agent \
 	ssh-store \
-	$(ROOTS_DIR) \
-	$(TESTS_DIR)
+	user-store

Modified: trunk/pkcs11/gck/Makefile.am
==============================================================================
--- trunk/pkcs11/gck/Makefile.am	(original)
+++ trunk/pkcs11/gck/Makefile.am	Sat Jan 17 20:17:52 2009
@@ -23,19 +23,21 @@
 	gck-crypto.c gck-crypto.h \
 	gck-data-asn1.c gck-data-asn1.h \
 	gck-data-der.c gck-data-der.h \
+	gck-data-file.c gck-data-file.h \
 	gck-data-openssl.c gck-data-openssl.h \
 	gck-data-pem.c gck-data-pem.h \
 	gck-data-types.h \
 	gck-factory.c gck-factory.h \
-	gck-file-store.c gck-file-store.h \
 	gck-file-tracker.c gck-file-tracker.h \
 	gck-key.c gck-key.h \
+	gck-login.c gck-login.h \
 	gck-manager.c gck-manager.h \
 	gck-memory-store.c gck-memory-store.h \
 	gck-module.c gck-module.h gck-module-ep.h \
 	gck-object.c gck-object.h \
 	gck-private-key.c gck-private-key.h \
 	gck-public-key.c gck-public-key.h \
+	gck-serializable.c gck-serializable.h \
 	gck-session.c gck-session.h \
 	gck-sexp.c gck-sexp.h \
 	gck-store.c gck-store.h \

Modified: trunk/pkcs11/gck/gck-certificate.c
==============================================================================
--- trunk/pkcs11/gck/gck-certificate.c	(original)
+++ trunk/pkcs11/gck/gck-certificate.c	Sat Jan 17 20:17:52 2009
@@ -31,6 +31,7 @@
 #include "gck-data-der.h"
 #include "gck-key.h"
 #include "gck-manager.h"
+#include "gck-serializable.h"
 #include "gck-sexp.h"
 #include "gck-util.h"
 
@@ -54,7 +55,10 @@
 
 static GQuark OID_BASIC_CONSTRAINTS;
 
-G_DEFINE_TYPE (GckCertificate, gck_certificate, GCK_TYPE_OBJECT);
+static void gck_certificate_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckCertificate, gck_certificate, GCK_TYPE_OBJECT, 0,
+               G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_certificate_serializable));
 
 /* -----------------------------------------------------------------------------
  * INTERNAL 
@@ -317,26 +321,23 @@
 	init_quarks ();
 }
 
-/* -----------------------------------------------------------------------------
- * PUBLIC 
- */
-
-gboolean
-gck_certificate_load_data (GckCertificate *self, const guchar *data, gsize n_data)
+static gboolean 
+gck_certificate_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
 {
+	GckCertificate *self = GCK_CERTIFICATE (base);
 	ASN1_TYPE asn1 = ASN1_TYPE_EMPTY;
 	GckDataResult res;
 	guchar *copy, *keydata;
 	gsize n_keydata;
 	gcry_sexp_t sexp;
 	GckSexp *wrapper;
-	
+		
 	g_return_val_if_fail (GCK_IS_CERTIFICATE (self), FALSE);
 	g_return_val_if_fail (data, FALSE);
 	g_return_val_if_fail (n_data, FALSE);
-	
+		
 	copy = g_memdup (data, n_data);
-	
+		
 	/* Parse the ASN1 data */
 	res = gck_data_der_read_certificate (copy, n_data, &asn1);
 	if (res != GCK_DATA_SUCCESS) {
@@ -344,7 +345,7 @@
 		g_free (copy);
 		return FALSE;
 	}
-	
+		
 	/* Generate a raw public key from our certificate */
 	keydata = gck_data_asn1_encode (asn1, "tbsCertificate.subjectPublicKeyInfo", &n_keydata, NULL);
 	g_return_val_if_fail (keydata, FALSE);
@@ -358,24 +359,50 @@
 		asn1_delete_structure (&asn1);
 		return FALSE;
 	}
-	
+		
 	/* Create ourselves a public key with that */
 	wrapper = gck_sexp_new (sexp);
 	if (!self->pv->key)
 		self->pv->key = gck_certificate_key_new (self);
 	gck_key_set_base_sexp (GCK_KEY (self->pv->key), wrapper);
 	gck_sexp_unref (wrapper);
-	
+		
 	g_free (self->pv->data);
 	self->pv->data = copy;
 	self->pv->n_data = n_data;
-	
+		
 	asn1_delete_structure (&self->pv->asn1);
 	self->pv->asn1 = asn1;
+		
+	return TRUE;
+}
+
+static gboolean 
+gck_certificate_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+	GckCertificate *self = GCK_CERTIFICATE (base);
 	
+	g_return_val_if_fail (GCK_IS_CERTIFICATE (self), FALSE);
+	g_return_val_if_fail (data, FALSE);
+	g_return_val_if_fail (n_data, FALSE);
+	
+	*n_data = self->pv->n_data;
+	*data = g_memdup (self->pv->data, self->pv->n_data);
 	return TRUE;
 }
 
+static void 
+gck_certificate_serializable (GckSerializableIface *iface)
+{
+	iface->extension = ".cer";
+	iface->load = gck_certificate_real_load;
+	iface->save = gck_certificate_real_save;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+
 gboolean
 gck_certificate_calc_category (GckCertificate *self, CK_ULONG* category)
 {

Modified: trunk/pkcs11/gck/gck-certificate.h
==============================================================================
--- trunk/pkcs11/gck/gck-certificate.h	(original)
+++ trunk/pkcs11/gck/gck-certificate.h	Sat Jan 17 20:17:52 2009
@@ -48,10 +48,6 @@
 
 GType                      gck_certificate_get_type               (void);
 
-gboolean                   gck_certificate_load_data              (GckCertificate *self, 
-                                                                   const guchar *data, 
-                                                                   gsize n_data);
-
 gboolean                   gck_certificate_calc_category          (GckCertificate *self, 
                                                                    CK_ULONG* category);
 

Modified: trunk/pkcs11/gck/gck-crypto.c
==============================================================================
--- trunk/pkcs11/gck/gck-crypto.c	(original)
+++ trunk/pkcs11/gck/gck-crypto.c	Sat Jan 17 20:17:52 2009
@@ -766,6 +766,86 @@
 	return ret;
 }
 
+static gcry_sexp_t
+rsa_numbers_to_public (gcry_sexp_t rsa)
+{
+	gcry_sexp_t pubkey = NULL;
+	gcry_mpi_t n, e;
+	gcry_error_t gcry;
+	
+	n = e = NULL;
+	
+	if (!gck_crypto_sexp_extract_mpi (rsa, &n, "n", NULL) || 
+	    !gck_crypto_sexp_extract_mpi (rsa, &e, "e", NULL))
+	    	goto done;
+	    	
+	gcry = gcry_sexp_build (&pubkey, NULL, "(public-key (rsa (n %m) (e %m)))",
+	                        n, e);
+	if (gcry)
+		goto done;
+	g_assert (pubkey);
+	
+done:
+	gcry_mpi_release (n);
+	gcry_mpi_release (e);
+
+	return pubkey;
+}
+
+static gcry_sexp_t
+dsa_numbers_to_public (gcry_sexp_t dsa)
+{
+	gcry_mpi_t p, q, g, y;
+	gcry_sexp_t pubkey = NULL;
+	gcry_error_t gcry;
+	
+	p = q = g = y = NULL;
+	
+	if (!gck_crypto_sexp_extract_mpi (dsa, &p, "p", NULL) || 
+	    !gck_crypto_sexp_extract_mpi (dsa, &q, "q", NULL) ||
+	    !gck_crypto_sexp_extract_mpi (dsa, &g, "g", NULL) ||
+	    !gck_crypto_sexp_extract_mpi (dsa, &y, "y", NULL))
+	    	goto done;
+	    	
+	gcry = gcry_sexp_build (&pubkey, NULL, "(public-key (dsa (p %m) (q %m) (g %m) (y %m)))",
+	                        p, q, g, y);
+	if (gcry)
+		goto done;
+	g_assert (pubkey);
+	
+done:
+	gcry_mpi_release (p);
+	gcry_mpi_release (q);
+	gcry_mpi_release (g);
+	gcry_mpi_release (y);
+
+	return pubkey;
+}
+
+gboolean
+gck_crypto_sexp_key_to_public (gcry_sexp_t privkey, gcry_sexp_t *pubkey)
+{
+	gcry_sexp_t numbers;
+	int algorithm;
+
+	if (!gck_crypto_sexp_parse_key (privkey, &algorithm, NULL, &numbers))
+		g_return_val_if_reached (FALSE);
+		
+	switch (algorithm) {
+	case GCRY_PK_RSA:
+		*pubkey = rsa_numbers_to_public (numbers);
+		break;
+	case GCRY_PK_DSA:
+		*pubkey = dsa_numbers_to_public (numbers);
+		break;
+	default:
+		g_return_val_if_reached (FALSE);
+	} 
+	
+	gcry_sexp_release (numbers);
+	return *pubkey ? TRUE : FALSE;
+}
+
 gboolean
 gck_crypto_sexp_extract_mpi (gcry_sexp_t sexp, gcry_mpi_t *mpi, ...)
 {
@@ -1274,7 +1354,7 @@
 	/* Cleanup in case of failure */
 	if (!ret) {
 		g_free (iv ? *iv : NULL);
-		g_free (key ? *key : NULL);
+		gcry_free (key ? *key : NULL);
 	}
 	
 	return ret;
@@ -1405,7 +1485,7 @@
 	/* Cleanup in case of failure */
 	if (!ret) {
 		g_free (iv ? *iv : NULL);
-		g_free (key ? *key : NULL);
+		gcry_free (key ? *key : NULL);
 	}
 	
 	return ret;

Modified: trunk/pkcs11/gck/gck-crypto.h
==============================================================================
--- trunk/pkcs11/gck/gck-crypto.h	(original)
+++ trunk/pkcs11/gck/gck-crypto.h	Sat Jan 17 20:17:52 2009
@@ -125,6 +125,9 @@
                                                                         gboolean *is_private, 
                                                                         gcry_sexp_t *numbers);
 
+gboolean                 gck_crypto_sexp_key_to_public                 (gcry_sexp_t sexp, 
+                                                                        gcry_sexp_t *pub);
+
 gboolean                 gck_crypto_sexp_extract_mpi                   (gcry_sexp_t sexp, 
                                                                         gcry_mpi_t *mpi, 
                                                                         ...) G_GNUC_NULL_TERMINATED;

Modified: trunk/pkcs11/gck/gck-data-asn1.c
==============================================================================
--- trunk/pkcs11/gck/gck-data-asn1.c	(original)
+++ trunk/pkcs11/gck/gck-data-asn1.c	Sat Jan 17 20:17:52 2009
@@ -346,8 +346,6 @@
 	return quark;
 }
 
-#ifdef UNTESTED_CODE
-
 gboolean
 gck_data_asn1_write_oid (ASN1_TYPE asn, const gchar *part, GQuark val)
 {
@@ -358,12 +356,9 @@
 	oid = g_quark_to_string (val);
 	g_return_val_if_fail (oid, FALSE);
 	
-	return gck_data_asn1_write_value (asn, part, (const guchar*)oid, 
-			                          1 /* any non-null value for OID */);
+	return gck_data_asn1_write_value (asn, part, (const guchar*)oid, strlen (oid));
 }
 
-#endif /* UNTESTED_CODE */
-
 gboolean
 gck_data_asn1_read_mpi (ASN1_TYPE asn, const gchar *part, gcry_mpi_t *mpi)
 {

Modified: trunk/pkcs11/gck/gck-data-der.c
==============================================================================
--- trunk/pkcs11/gck/gck-data-der.c	(original)
+++ trunk/pkcs11/gck/gck-data-der.c	Sat Jan 17 20:17:52 2009
@@ -28,6 +28,8 @@
 #include "gck-data-der.h"
 #include "gck-data-types.h"
 
+#include "common/gkr-secure-memory.h"
+
 #include <glib.h>
 #include <gcrypt.h>
 #include <libtasn1.h>
@@ -524,6 +526,180 @@
 	return res;
 }
 
+GckDataResult
+gck_data_der_read_private_pkcs8_plain (const guchar *data, gsize n_data, gcry_sexp_t *s_key)
+{
+	ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+	GckDataResult ret;
+	int algorithm;
+	GQuark key_algo;
+	const guchar *keydata;
+	gsize n_keydata;
+	const guchar *params;
+	gsize n_params;
+	
+	ret = GCK_DATA_UNRECOGNIZED;
+	
+	init_quarks ();
+	
+	asn = gck_data_asn1_decode ("PKIX1.pkcs-8-PrivateKeyInfo", data, n_data);
+	if (!asn)
+		goto done;
+
+	ret = GCK_DATA_FAILURE;
+	algorithm = 0;
+		
+	key_algo = gck_data_asn1_read_oid (asn, "privateKeyAlgorithm.algorithm");
+  	if (!key_algo)
+  		goto done;
+  	else if (key_algo == OID_PKIX1_RSA)
+  		algorithm = GCRY_PK_RSA;
+  	else if (key_algo == OID_PKIX1_DSA)
+  		algorithm = GCRY_PK_DSA;
+  		
+  	if (!algorithm) {
+  		ret = GCK_DATA_UNRECOGNIZED;
+  		goto done;
+  	}
+
+	keydata = gck_data_asn1_read_content (asn, data, n_data, "privateKey", &n_keydata);
+	if (!keydata)
+		goto done;
+		
+	params = gck_data_asn1_read_element (asn, data, n_data, "privateKeyAlgorithm.parameters", 
+	                                     &n_params);
+		
+	ret = GCK_DATA_SUCCESS;
+	
+done:
+	if (ret == GCK_DATA_SUCCESS) {		
+		switch (algorithm) {
+		case GCRY_PK_RSA:
+			ret = gck_data_der_read_private_key_rsa (keydata, n_keydata, s_key);
+			break;
+		case GCRY_PK_DSA:
+			/* Try the normal one block format */
+			ret = gck_data_der_read_private_key_dsa (keydata, n_keydata, s_key);
+			
+			/* Otherwise try the two part format that everyone seems to like */
+			if (ret == GCK_DATA_UNRECOGNIZED && params && n_params)
+				ret = gck_data_der_read_private_key_dsa_parts (keydata, n_keydata, 
+				                                               params, n_params, s_key);
+			break;
+		default:
+			g_message ("invalid or unsupported key type in PKCS#8 key");
+			ret = GCK_DATA_UNRECOGNIZED;
+			break;
+		};
+		
+	} else if (ret == GCK_DATA_FAILURE) {
+		g_message ("invalid PKCS#8 key");
+	}
+	
+	if (asn)
+		asn1_delete_structure (&asn);
+	
+	return ret;
+}
+
+GckDataResult
+gck_data_der_read_private_pkcs8_crypted (const guchar *data, gsize n_data, const gchar *password, 
+                                         gsize n_password, gcry_sexp_t *s_key)
+{
+	ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+	gcry_cipher_hd_t cih = NULL;
+	gcry_error_t gcry;
+	GckDataResult ret, r;
+	GQuark scheme;
+	guchar *crypted = NULL;
+	const guchar *params;
+	gsize n_crypted, n_params;
+	gint l;
+
+	init_quarks ();
+
+	ret = GCK_DATA_UNRECOGNIZED;
+	
+	asn = gck_data_asn1_decode ("PKIX1.pkcs-8-EncryptedPrivateKeyInfo", data, n_data);
+	if (!asn)
+		goto done;
+
+	ret = GCK_DATA_FAILURE;
+
+	/* Figure out the type of encryption */
+	scheme = gck_data_asn1_read_oid (asn, "encryptionAlgorithm.algorithm");
+	if (!scheme)
+		goto done;
+		
+	params = gck_data_asn1_read_element (asn, data, n_data, "encryptionAlgorithm.parameters", &n_params);
+	if (!params)
+		goto done;
+
+	/* 
+	 * Parse the encryption stuff into a cipher. 
+	 */
+	r = gck_data_der_read_cipher (scheme, password, n_password, params, n_params, &cih);
+	if (r == GCK_DATA_UNRECOGNIZED) {
+		ret = GCK_DATA_FAILURE;
+		goto done;
+	} else if (r != GCK_DATA_SUCCESS) {
+		ret = r;
+		goto done;
+	}
+			
+	crypted = gck_data_asn1_read_value (asn, "encryptedData", &n_crypted, gkr_secure_realloc);
+	if (!crypted)
+		goto done;
+	
+	gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
+	gcry_cipher_close (cih);
+	cih = NULL;
+		
+	if (gcry != 0) {
+		g_warning ("couldn't decrypt pkcs8 data: %s", gcry_strerror (gcry));
+		goto done;
+	}
+		
+	/* Unpad the DER data */
+	l = gck_data_asn1_element_length (crypted, n_crypted);
+	if (l <= 0 || l > n_crypted) {
+		ret = GCK_DATA_LOCKED;
+		goto done;
+	} 
+	n_crypted = l;
+		
+	/* Try to parse the resulting key */
+	ret = gck_data_der_read_private_pkcs8_plain (crypted, n_crypted, s_key);
+	gkr_secure_free (crypted);
+	crypted = NULL;
+		
+	/* If unrecognized we assume bad password */
+	if (ret == GCK_DATA_UNRECOGNIZED) 
+		ret = GCK_DATA_LOCKED;
+
+done:
+	if (cih)
+		gcry_cipher_close (cih);
+	if (asn)
+		asn1_delete_structure (&asn);
+	gkr_secure_free (crypted);
+		
+	return ret;
+}
+
+GckDataResult
+gck_data_der_read_private_pkcs8 (const guchar *data, gsize n_data, const gchar *password, 
+                                 gsize n_password, gcry_sexp_t *s_key)
+{
+	GckDataResult res;
+
+	res = gck_data_der_read_private_pkcs8_crypted (data, n_data, password, n_password, s_key);
+	if (res == GCK_DATA_UNRECOGNIZED) 
+		res = gck_data_der_read_private_pkcs8_plain (data, n_data, s_key);
+
+	return res;
+}
+
 guchar*
 gck_data_der_write_public_key_rsa (gcry_sexp_t s_key, gsize *len)
 {
@@ -823,6 +999,199 @@
 	}
 }
 
+static gcry_cipher_hd_t
+prepare_and_encode_pkcs8_cipher (ASN1_TYPE asn, const gchar *password, 
+                                 gsize n_password, gsize *n_block)
+{
+	ASN1_TYPE asn1_params;
+	gcry_cipher_hd_t cih;
+	guchar salt[8];
+	gcry_error_t gcry;
+	guchar *key, *iv, *portion;
+	gsize n_key, n_portion;
+	int iterations, res;
+	
+	init_quarks ();
+
+	/* Make sure the encryption algorithm works */
+	g_return_val_if_fail (gcry_cipher_algo_info (OID_PKCS12_PBE_3DES_SHA1, 
+	                                             GCRYCTL_TEST_ALGO, NULL, 0), NULL);
+
+	/* The encryption algorithm */
+	if(!gck_data_asn1_write_oid (asn, "encryptionAlgorithm.algorithm", 
+	                             OID_PKCS12_PBE_3DES_SHA1))
+		g_return_val_if_reached (NULL); 
+
+	/* Randomize some input for the password based secret */
+	iterations = 1000 + (int) (1000.0 * rand () / (RAND_MAX + 1.0));
+	gcry_create_nonce (salt, sizeof (salt));
+
+	/* Allocate space for the key and iv */
+	n_key = gcry_cipher_get_algo_keylen (GCRY_CIPHER_3DES);
+	*n_block = gcry_cipher_get_algo_blklen (GCRY_MD_SHA1);
+	g_return_val_if_fail (n_key && *n_block, NULL);
+		
+	if (!gck_crypto_symkey_generate_pkcs12 (GCRY_CIPHER_3DES, GCRY_MD_SHA1, 
+	                                        password, n_password, salt, 
+	                                        sizeof (salt), iterations, &key, &iv))
+		g_return_val_if_reached (NULL);
+
+	/* Now write out the parameters */	
+	res = asn1_create_element (gck_data_asn1_get_pkix_asn1type (),
+	                           "PKIX1.pkcs-12-PbeParams", &asn1_params);
+	g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+	if (!gck_data_asn1_write_value (asn1_params, "salt", salt, sizeof (salt)))
+		g_return_val_if_reached (NULL);
+	if (!gck_data_asn1_write_uint (asn1_params, "iterations", iterations))
+		g_return_val_if_reached (NULL);
+	portion = gck_data_asn1_encode (asn1_params, "", &n_portion, NULL);
+	g_return_val_if_fail (portion, NULL); 
+	
+	if (!gck_data_asn1_write_value (asn, "encryptionAlgorithm.parameters", portion, n_portion))
+		g_return_val_if_reached (NULL);
+	g_free (portion);
+	
+	/* Now make a cipher that matches what we wrote out */
+	gcry = gcry_cipher_open (&cih, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
+	g_return_val_if_fail (gcry == 0, NULL);
+	g_return_val_if_fail (cih, NULL);
+	
+	gcry_cipher_setiv (cih, iv, *n_block);
+	gcry_cipher_setkey (cih, key, n_key);
+	
+	g_free (iv);
+	gcry_free (key);
+	asn1_delete_structure (&asn1_params);
+	
+	return cih;
+}
+
+guchar*
+gck_data_der_write_private_pkcs8_plain (gcry_sexp_t skey, gsize *n_data)
+{
+	ASN1_TYPE asn;
+	int res, algorithm;
+	gboolean is_priv;
+	GQuark oid;
+	guchar *params, *key, *data;
+	gsize n_params, n_key;
+	
+	init_quarks ();
+
+	/* Parse and check that the key is for real */
+	if (!gck_crypto_sexp_parse_key (skey, &algorithm, &is_priv, NULL))
+		g_return_val_if_reached (NULL);
+	g_return_val_if_fail (is_priv == TRUE, NULL);
+	
+	res = asn1_create_element (gck_data_asn1_get_pkix_asn1type (), 
+	                           "PKIX1.pkcs-8-PrivateKeyInfo", &asn);
+	g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+	
+	/* Write out the version */
+	if (!gck_data_asn1_write_uint (asn, "version", 1))
+		g_return_val_if_reached (NULL);
+	
+	/* Per algorithm differences */
+	switch (algorithm)
+	{
+	/* RSA gets encoded in a standard simple way */
+	case GCRY_PK_RSA:
+		oid = OID_PKIX1_RSA;
+		params = NULL;
+		n_params = 0;
+		key = gck_data_der_write_private_key_rsa (skey, &n_key);
+		break;
+		
+	/* DSA gets incoded with the params seperate */
+	case GCRY_PK_DSA:
+		oid = OID_PKIX1_DSA;
+		key = gck_data_der_write_private_key_dsa_part (skey, &n_key);
+		params = gck_data_der_write_private_key_dsa_params (skey, &n_params);
+		break;
+		
+	default:
+		g_warning ("trying to serialize unsupported private key algorithm: %d", algorithm);
+		return NULL;
+	};
+	
+	/* Write out the algorithm */
+	if (!gck_data_asn1_write_oid (asn, "privateKeyAlgorithm.algorithm", oid))
+		g_return_val_if_reached (NULL);
+
+	/* Write out the parameters */
+	if (!gck_data_asn1_write_value (asn, "privateKeyAlgorithm.parameters", params, n_params))
+		g_return_val_if_reached (NULL);
+	gkr_secure_free (params);
+	
+	/* Write out the key portion */
+	if (!gck_data_asn1_write_value (asn, "privateKey", key, n_key))
+		g_return_val_if_reached (NULL);
+	gkr_secure_free (key);
+	
+	/* Add an empty attributes field */
+	if (!gck_data_asn1_write_value (asn, "attributes", NULL, 0))
+		g_return_val_if_reached (NULL);
+	
+	data = gck_data_asn1_encode (asn, "", n_data, NULL);
+	g_return_val_if_fail (data, NULL); 
+	
+	asn1_delete_structure (&asn);
+	
+	return data;
+}
+
+guchar*
+gck_data_der_write_private_pkcs8_crypted (gcry_sexp_t skey, const gchar *password,
+                                          gsize n_password, gsize *n_data)
+{
+	gcry_error_t gcry;
+	gcry_cipher_hd_t cih;
+	ASN1_TYPE asn;
+	int res;
+	guchar *key, *data; 
+	gsize n_key, block = 0;
+
+	/* Encode the key in normal pkcs8 fashion */
+	key = gck_data_der_write_private_pkcs8_plain (skey, &n_key);
+	
+	res = asn1_create_element (gck_data_asn1_get_pkix_asn1type (), 
+	                           "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &asn);
+	g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+	
+	/* Create a and write out a cipher used for encryption */
+	cih = prepare_and_encode_pkcs8_cipher (asn, password, n_password, &block);
+	g_return_val_if_fail (cih, NULL);
+	
+	/* Pad the block of data */
+	if(block > 1) {
+		gsize pad;
+		guchar *padded;
+		
+		pad = block - (n_key % block);
+		if (pad == 0)
+			pad = block;
+		padded = g_realloc (key, n_key + pad);
+		memset (padded + n_key, pad, pad);
+		key = padded;
+		n_key += pad;
+	}
+	
+	gcry = gcry_cipher_encrypt (cih, key, n_key, NULL, 0);
+	g_return_val_if_fail (gcry == 0, NULL);
+	
+	gcry_cipher_close (cih);
+	
+	res = asn1_write_value (asn, "encryptedData", key, n_key);
+	g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+	
+	data = gck_data_asn1_encode (asn, "", n_data, NULL);
+	g_return_val_if_fail (data, NULL); 
+
+	asn1_delete_structure (&asn);
+	
+	return data;
+}
+
 /* -----------------------------------------------------------------------------
  * CERTIFICATES
  */
@@ -943,6 +1312,8 @@
 	return ret;
 }
 
+#endif /* UNTESTED CODE */
+
 guchar*
 gck_data_der_write_certificate (ASN1_TYPE asn1, gsize *n_data)
 {
@@ -957,7 +1328,7 @@
  */
  
 GckDataResult
-gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password, 
+gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password, gsize n_password,
                           const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
 {
 	GckDataResult ret = GCK_DATA_UNRECOGNIZED;
@@ -971,20 +1342,20 @@
 	/* PKCS#5 PBE */
 	if (oid_scheme == OID_PBE_MD2_DES_CBC)
 		ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
-		                                          GCRY_MD_MD2, password, data, n_data, cih);
+		                                          GCRY_MD_MD2, password, n_password, data, n_data, cih);
 
 	else if (oid_scheme == OID_PBE_MD2_RC2_CBC)
 		/* RC2-64 has no implementation in libgcrypt */
 		ret = GCK_DATA_UNRECOGNIZED;
 	else if (oid_scheme == OID_PBE_MD5_DES_CBC)
 		ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
-		                                          GCRY_MD_MD5, password, data, n_data, cih);
+		                                          GCRY_MD_MD5, password, n_password, data, n_data, cih);
 	else if (oid_scheme == OID_PBE_MD5_RC2_CBC)
 		/* RC2-64 has no implementation in libgcrypt */
 		ret = GCK_DATA_UNRECOGNIZED;
 	else if (oid_scheme == OID_PBE_SHA1_DES_CBC)
 		ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
-		                                          GCRY_MD_SHA1, password, data, n_data, cih);
+		                                          GCRY_MD_SHA1, password, n_password, data, n_data, cih);
 	else if (oid_scheme == OID_PBE_SHA1_RC2_CBC)
 		/* RC2-64 has no implementation in libgcrypt */
 		ret = GCK_DATA_UNRECOGNIZED;
@@ -992,29 +1363,29 @@
 	
 	/* PKCS#5 PBES2 */
 	else if (oid_scheme == OID_PBES2)
-		ret = gck_data_der_read_cipher_pkcs5_pbes2 (password, data, n_data, cih);
+		ret = gck_data_der_read_cipher_pkcs5_pbes2 (password, n_password, data, n_data, cih);
 
 		
 	/* PKCS#12 PBE */
 	else if (oid_scheme == OID_PKCS12_PBE_ARCFOUR_SHA1)
 		ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 
-                                                   password, data, n_data, cih);
+		                                           password, n_password, data, n_data, cih);
 	else if (oid_scheme == OID_PKCS12_PBE_RC4_40_SHA1)
 		/* RC4-40 has no implementation in libgcrypt */;
 
 	else if (oid_scheme == OID_PKCS12_PBE_3DES_SHA1)
 		ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 
-                                                   password, data, n_data, cih);
+		                                           password, n_password, data, n_data, cih);
 	else if (oid_scheme == OID_PKCS12_PBE_2DES_SHA1) 
 		/* 2DES has no implementation in libgcrypt */;
 		
 	else if (oid_scheme == OID_PKCS12_PBE_RC2_128_SHA1)
 		ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_128, GCRY_CIPHER_MODE_CBC, 
-                                                   password, data, n_data, cih);
+		                                           password, n_password, data, n_data, cih);
 
 	else if (oid_scheme == OID_PKCS12_PBE_RC2_40_SHA1)
 		ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_40, GCRY_CIPHER_MODE_CBC, 
-                                                   password, data, n_data, cih);
+		                                           password, n_password, data, n_data, cih);
 
 	if (ret == GCK_DATA_UNRECOGNIZED)
     		g_message ("unsupported or unrecognized cipher oid: %s", g_quark_to_string (oid_scheme));
@@ -1022,8 +1393,8 @@
 }
 
 GckDataResult
-gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode, 
-                                    int hash_algo, const gchar *password, const guchar *data, 
+gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode, int hash_algo, 
+                                    const gchar *password, gsize n_password, const guchar *data, 
                                     gsize n_data, gcry_cipher_hd_t *cih)
 {
 	ASN1_TYPE asn = ASN1_TYPE_EMPTY;
@@ -1064,7 +1435,7 @@
 	g_return_val_if_fail (n_key > 0, GCK_DATA_FAILURE);
 	n_block = gcry_cipher_get_algo_blklen (cipher_algo);
 		
-	if (!gck_crypto_symkey_generate_pbe (cipher_algo, hash_algo, password, salt,
+	if (!gck_crypto_symkey_generate_pbe (cipher_algo, hash_algo, password, n_password, salt,
 	                                     n_salt, iterations, &key, n_block > 1 ? &iv : NULL))
 		goto done;
 		
@@ -1157,7 +1528,7 @@
 }
 
 static GckDataResult
-setup_pkcs5_pbkdf2_params (const gchar *password, const guchar *data, 
+setup_pkcs5_pbkdf2_params (const gchar *password, gsize n_password, const guchar *data, 
                            gsize n_data, int cipher_algo, gcry_cipher_hd_t cih)
 {
 	ASN1_TYPE asn = ASN1_TYPE_EMPTY;
@@ -1185,7 +1556,7 @@
 	if (!salt)
 		goto done;
 				
-	if (!gck_crypto_symkey_generate_pbkdf2 (cipher_algo, GCRY_MD_SHA1, password, 
+	if (!gck_crypto_symkey_generate_pbkdf2 (cipher_algo, GCRY_MD_SHA1, password, n_password, 
 	                                        salt, n_salt, iterations, &key, NULL))
 		goto done;
 
@@ -1208,7 +1579,7 @@
 }
 
 GckDataResult
-gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, const guchar *data, 
+gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, gsize n_password, const guchar *data, 
                                       gsize n_data, gcry_cipher_hd_t *cih)
 {
 	ASN1_TYPE asn = ASN1_TYPE_EMPTY;
@@ -1298,7 +1669,7 @@
 	                                &beg, &end) != ASN1_SUCCESS)
 		goto done;
 	
-	ret = setup_pkcs5_pbkdf2_params (password, data + beg, end - beg + 1, algo, *cih);
+	ret = setup_pkcs5_pbkdf2_params (password, n_password, data + beg, end - beg + 1, algo, *cih);
 
 done:
 	if (ret != GCK_DATA_SUCCESS && *cih) {
@@ -1314,7 +1685,8 @@
 
 GckDataResult
 gck_data_der_read_cipher_pkcs12_pbe (int cipher_algo, int cipher_mode, const gchar *password, 
-                                     const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
+                                     gsize n_password, const guchar *data, gsize n_data, 
+                                     gcry_cipher_hd_t *cih)
 {
 	ASN1_TYPE asn = ASN1_TYPE_EMPTY;
 	gcry_error_t gcry;
@@ -1353,8 +1725,8 @@
 	n_key = gcry_cipher_get_algo_keylen (cipher_algo);
 	
 	/* Generate IV and key using salt read above */
-	if (!gck_crypto_symkey_generate_pkcs12 (cipher_algo, GCRY_MD_SHA1, password,
-	                                        salt, n_salt, iterations, &key, 
+	if (!gck_crypto_symkey_generate_pkcs12 (cipher_algo, GCRY_MD_SHA1, password, 
+	                                        n_password, salt, n_salt, iterations, &key, 
 	                                        n_block > 1 ? &iv : NULL))
 		goto done;
 		
@@ -1384,5 +1756,3 @@
 	
 	return ret;
 }
-
-#endif /* UNTESTED_CODE */

Modified: trunk/pkcs11/gck/gck-data-der.h
==============================================================================
--- trunk/pkcs11/gck/gck-data-der.h	(original)
+++ trunk/pkcs11/gck/gck-data-der.h	Sat Jan 17 20:17:52 2009
@@ -47,6 +47,17 @@
 GckDataResult      gck_data_der_read_private_key             (const guchar *data, gsize n_data, 
                                                               gcry_sexp_t *s_key);
 
+GckDataResult      gck_data_der_read_private_pkcs8           (const guchar *data, gsize n_data,
+                                                              const gchar *password, gsize n_password, 
+                                                              gcry_sexp_t *s_key); 
+
+GckDataResult      gck_data_der_read_private_pkcs8_plain     (const guchar *data, gsize n_data,
+                                                              gcry_sexp_t *s_key); 
+
+GckDataResult      gck_data_der_read_private_pkcs8_crypted   (const guchar *data, gsize n_data,
+                                                              const gchar *password, gsize n_password, 
+                                                              gcry_sexp_t *s_key); 
+
 guchar*            gck_data_der_write_private_key_rsa        (gcry_sexp_t s_key, gsize *n_data);
 
 guchar*            gck_data_der_write_private_key_dsa        (gcry_sexp_t s_key, gsize *len);
@@ -57,6 +68,10 @@
 
 guchar*            gck_data_der_write_private_key            (gcry_sexp_t s_key, gsize *n_data);
 
+guchar*            gck_data_der_write_private_pkcs8_plain    (gcry_sexp_t skey, gsize *n_data);
+
+guchar*            gck_data_der_write_private_pkcs8_crypted  (gcry_sexp_t skey, const gchar *password,
+                                                              gsize n_password, gsize *n_data);
 
 /* -----------------------------------------------------------------------------
  * PUBLIC KEYS
@@ -107,20 +122,20 @@
  * CIPHERS
  */
  
-GckDataResult      gck_data_der_read_cipher                 (GQuark oid_scheme, const gchar *password, 
-                                                             const guchar *data, gsize n_data, 
+GckDataResult      gck_data_der_read_cipher                 (GQuark oid_scheme, const gchar *password,
+                                                             gsize n_password, const guchar *data, gsize n_data, 
                                                              gcry_cipher_hd_t *cih);
 
 GckDataResult      gck_data_der_read_cipher_pkcs5_pbe       (int cipher_algo, int cipher_mode, 
-                                                             int hash_algo, const gchar *password, 
+                                                             int hash_algo, const gchar *password, gsize n_password,
                                                              const guchar *data, gsize n_data, 
                                                              gcry_cipher_hd_t *cih);
 
-GckDataResult      gck_data_der_read_cipher_pkcs5_pbes2     (const gchar *password, const guchar *data, 
+GckDataResult      gck_data_der_read_cipher_pkcs5_pbes2     (const gchar *password, gsize n_password, const guchar *data, 
                                                              gsize n_data, gcry_cipher_hd_t *cih);
 
 GckDataResult      gck_data_der_read_cipher_pkcs12_pbe      (int cipher_algo, int cipher_mode, 
-                                                             const gchar *password, const guchar *data, 
+                                                             const gchar *password, gsize n_password, const guchar *data, 
                                                              gsize n_data, gcry_cipher_hd_t *cih);
 
 #endif /*GKRPKIXDER_H_*/

Added: trunk/pkcs11/gck/gck-data-file.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-data-file.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,1432 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-attributes.h"
+#include "gck-crypto.h"
+#include "gck-data-file.h"
+#include "gck-data-types.h"
+#include "gck-marshal.h"
+#include "gck-util.h"
+
+#include "common/gkr-buffer.h"
+#include "common/gkr-secure-memory.h"
+
+#include <glib/gstdio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h> 
+
+#include <gcrypt.h>
+
+enum {
+	ENTRY_ADDED,
+	ENTRY_CHANGED,
+	ENTRY_REMOVED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GckDataFile {
+	GObject parent;
+	
+	/* The data itself */
+	GHashTable *identifiers;
+	GHashTable *privates;
+	GHashTable *publics;
+	GList *unknowns;
+	
+	/* All the sections seen */
+	guint sections;
+	gboolean incomplete;
+	
+	/* Stuff added/notseen on this read */
+	GHashTable *added;
+	GHashTable *checks;
+};
+
+typedef struct _UnknownBlock {
+	guint type;
+	GkrBuffer buffer;
+} UnknownBlock;
+
+G_DEFINE_TYPE (GckDataFile, gck_data_file, G_TYPE_OBJECT);
+
+#define PUBLIC_ALLOC (GkrBufferAllocator)g_realloc
+#define PRIVATE_ALLOC (GkrBufferAllocator)gkr_secure_realloc
+
+typedef GckDataResult (*BlockFunc) (guint block, GkrBuffer *buffer, GckLogin *login, gpointer user_data);
+
+#define FILE_HEADER ((const guchar*)"Gnome Keyring Store 2\n\r\0")
+#define FILE_HEADER_LEN 24
+
+#define FILE_BLOCK_INDEX    0x49445832  /* ie: "IDX2" */
+#define FILE_BLOCK_PRIVATE  0x50525632  /* ie: "PRV2" */
+#define FILE_BLOCK_PUBLIC   0x50554232  /* ie: "PUB2" */
+
+#define UNUSED_VALUE  GUINT_TO_POINTER (1)
+
+/* -----------------------------------------------------------------------------
+ * HELPERS 
+ */
+
+static void
+attribute_free (gpointer data)
+{
+	CK_ATTRIBUTE_PTR attr = data;
+	if (attr) {
+		g_free (attr->pValue);
+		g_slice_free (CK_ATTRIBUTE, attr);
+	}
+}
+
+static CK_ATTRIBUTE_PTR
+attribute_dup (CK_ATTRIBUTE_PTR attr)
+{
+	CK_ATTRIBUTE_PTR copy;
+	g_assert (attr);
+	copy = g_slice_new (CK_ATTRIBUTE);
+	copy->ulValueLen = attr->ulValueLen;
+	copy->pValue = g_memdup (attr->pValue, copy->ulValueLen);
+	copy->type = attr->type;
+	return copy;
+}
+
+static GHashTable*
+attributes_new (void)
+{
+	return g_hash_table_new_full (gck_util_ulong_hash, gck_util_ulong_equal, NULL, attribute_free);
+}
+
+static GHashTable*
+entries_new (void)
+{
+	return g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_hash_table_unref);
+}
+
+static gboolean
+read_all_bytes (int fd, guchar *buf, gsize len)
+{
+	gsize all = len;
+	int res;
+	
+	while (len > 0) {
+		
+		res = read (fd, buf, len);
+		if (res <= 0) {
+			if (errno == EAGAIN && errno == EINTR)
+				continue;
+			if (res < 0 || len != all)
+				g_warning ("couldn't read %u bytes from store file: %s", 
+				           (guint)all, g_strerror (errno));
+			return FALSE;
+		} else  {
+			len -= res;
+			buf += res;
+		}
+	}
+	
+	return TRUE;
+}
+
+static gboolean
+write_all_bytes (int fd, const guchar *buf, gsize len)
+{
+	gsize all = len;
+	int res;
+	
+	while (len > 0) {
+		
+		res = write (fd, buf, len);
+
+		if (res <= 0) {
+			if (errno == EAGAIN && errno == EINTR)
+				continue;
+			g_warning ("couldn't write %u bytes to store file: %s", 
+			           (guint)all, res < 0 ? g_strerror (errno) : "");
+			return FALSE;
+		} else  {
+			len -= res;
+			buf += res;
+		}
+	}
+	
+	return TRUE;
+}
+
+static GckDataResult
+parse_file_blocks (int file, BlockFunc block_func, GckLogin *login, gpointer user_data)
+{
+	gchar header[FILE_HEADER_LEN];
+	GckDataResult res;
+	GkrBuffer buffer;
+	guint32 block;
+	guint32 length;
+	gsize offset;
+	
+	g_assert (file != -1);
+	g_assert (block_func);
+	
+	/* Zero length file is valid */
+	if (!read_all_bytes (file, (guchar*)header, FILE_HEADER_LEN))
+		return TRUE;
+	
+	/* Check the header */
+	if (memcmp (header, FILE_HEADER, FILE_HEADER_LEN) != 0) {
+		g_message ("invalid header in store file");
+		return FALSE;
+	}
+
+	gkr_buffer_init_full (&buffer, 1024, (GkrBufferAllocator)g_realloc);
+
+	res = GCK_DATA_SUCCESS;
+	for (;;) {
+
+		gkr_buffer_reset (&buffer);
+		gkr_buffer_resize (&buffer, 8);
+		offset = 0;
+
+		/* Read in a set of bytes */
+		if (!read_all_bytes (file, buffer.buf, 8)) {
+			res = GCK_DATA_SUCCESS; /* end of file */
+			break;
+		}
+		
+		/* Decode it as the number of bytes in the next section */
+		if (!gkr_buffer_get_uint32 (&buffer, offset, &offset, &length) ||
+		    !gkr_buffer_get_uint32 (&buffer, offset, &offset, &block) || 
+		    length < 8) {
+			res = GCK_DATA_SUCCESS;
+			g_message ("invalid block size or length in store file");
+			break;
+		}
+		
+		/* Read in that amount of bytes */
+		gkr_buffer_resize (&buffer, length - 8);
+		if (!read_all_bytes (file, buffer.buf, length - 8)) {
+			res = GCK_DATA_FAILURE;
+			break;
+		}
+
+		res = (block_func) (block, &buffer, login, user_data);
+		if (res != GCK_DATA_SUCCESS)
+			break;
+	}
+	
+	gkr_buffer_uninit (&buffer);
+	return res;
+}
+
+static gboolean
+write_file_block (int file, guint block, GkrBuffer *buffer)
+{
+	GkrBuffer header;
+	gboolean ret;
+	
+	g_assert (file != -1);
+	g_assert (buffer);
+	
+	/* Write out the 8 bytes of header */
+	gkr_buffer_init_full (&header, 8, (GkrBufferAllocator)g_realloc);
+	gkr_buffer_add_uint32 (&header, buffer->len + 8);
+	gkr_buffer_add_uint32 (&header, block);
+	g_assert (!gkr_buffer_has_error (&header));
+	g_assert (header.len == 8);
+	ret = write_all_bytes (file, header.buf, header.len);
+	gkr_buffer_uninit (&header);
+	
+	if (ret != TRUE)
+		return FALSE;
+	
+	/* Now write out the remainder of the data */
+	return write_all_bytes (file, buffer->buf, buffer->len);
+}
+
+static gboolean
+hash_buffer (GkrBuffer *buffer)
+{
+	const gchar *salgo;
+	gsize length;
+	guchar *hash;
+	gsize n_hash;
+	int algo;
+	
+	/* The length needs to be the first thing in the buffer */
+	g_assert (buffer->len > 4);
+	g_assert (gkr_buffer_decode_uint32 (buffer->buf) == buffer->len);
+	
+	length = buffer->len;
+	
+	algo = GCRY_MD_SHA256;
+	salgo = gcry_md_algo_name (algo);
+	g_return_val_if_fail (salgo, FALSE);
+	n_hash = gcry_md_get_algo_dlen (algo);
+	g_return_val_if_fail (n_hash > 0, FALSE);
+	
+	gkr_buffer_add_string (buffer, salgo);
+	hash = gkr_buffer_add_byte_array_empty (buffer, n_hash);
+	g_return_val_if_fail (hash, FALSE);
+	
+	gcry_md_hash_buffer (algo, hash, buffer->buf, length);
+	return TRUE;
+}
+
+static gboolean
+validate_buffer (GkrBuffer *buffer, gsize *offset)
+{
+	const guchar *hash;
+	gchar *salgo, *check;
+	gsize n_hash, hash_offset;
+	guint32 length;
+	int algo;
+	
+	g_assert (buffer);
+	g_assert (offset);
+	
+	*offset = 0;
+	
+	if (!gkr_buffer_get_uint32 (buffer, *offset, offset, &length) || 
+	    !gkr_buffer_get_string (buffer, length, &hash_offset, &salgo, PUBLIC_ALLOC))
+		return FALSE;
+	
+	algo = gcry_md_map_name (salgo);
+	if (algo == 0) {
+		g_warning ("unsupported hash algorithm: %s", salgo);
+		g_free (salgo);
+		return FALSE;
+	}
+	g_free (salgo);
+	
+	if (!gkr_buffer_get_byte_array (buffer, hash_offset, &hash_offset, &hash, &n_hash))
+		return FALSE;
+	
+	if (n_hash != gcry_md_get_algo_dlen (algo)) {
+		g_warning ("invalid hash length in store file");
+		return FALSE;
+	}
+	
+	check = g_malloc0 (n_hash);
+	gcry_md_hash_buffer (algo, check, buffer->buf, length);
+	if (memcmp (check, hash, n_hash) != 0)
+		return FALSE;
+	
+	return TRUE;
+}
+
+static gboolean
+create_cipher (GckLogin *login, int calgo, int halgo, const guchar *salt, 
+               gsize n_salt, guint iterations, gcry_cipher_hd_t *cipher)
+{
+	gsize n_key, n_block;
+	const gchar *password;
+	gsize n_password;
+	guchar *key, *iv;
+	gcry_error_t gcry;
+	
+	g_assert (login);
+	g_assert (salt);
+	g_assert (cipher);
+
+	n_key = gcry_cipher_get_algo_keylen (calgo);
+	g_return_val_if_fail (n_key, FALSE);
+	n_block = gcry_cipher_get_algo_blklen (calgo);
+	g_return_val_if_fail (n_block, FALSE);
+
+	/* Allocate memory for the keys */
+	key = gcry_malloc_secure (n_key);
+	g_return_val_if_fail (key, FALSE);
+	iv = g_malloc0 (n_block);
+	
+	password = gck_login_get_password (login, &n_password);
+	
+	if (!gck_crypto_symkey_generate_simple (calgo, halgo, password, n_password, 
+	                                        salt, n_salt, iterations, &key, &iv)) {
+		gcry_free (key);
+		g_free (iv);
+		return FALSE;
+	}
+	
+	gcry = gcry_cipher_open (cipher, calgo, GCRY_CIPHER_MODE_CBC, 0);
+	if (gcry) {
+		g_warning ("couldn't create cipher context: %s", gcry_strerror (gcry));
+		gcry_free (key);
+		g_free (iv);
+		return FALSE;
+	}
+
+	gcry = gcry_cipher_setkey (*cipher, key, n_key);
+	g_return_val_if_fail (!gcry, FALSE);
+	gcry_free (key);
+
+	gcry = gcry_cipher_setiv (*cipher, iv, n_block);
+	g_return_val_if_fail (!gcry, FALSE);
+	g_free (iv);
+	
+	return TRUE;
+}
+
+static gboolean
+encrypt_buffer (GkrBuffer *input, GckLogin *login, GkrBuffer *output)
+{
+	gcry_cipher_hd_t cipher;
+	gcry_error_t gcry;
+	guchar salt[8];
+	guint32 iterations;
+	int calgo, halgo;
+	const gchar *salgo;
+	guchar *dest;
+	gsize n_block;
+	
+	g_assert (input);
+	g_assert (output);
+	g_assert (login);
+	
+	/* The algorithms we're going to use */
+	calgo = GCRY_CIPHER_AES128;
+	halgo = GCRY_MD_SHA256;
+	
+	/* Prepare us some salt */
+	gcry_create_nonce (salt, sizeof (salt));
+	
+	/* Prepare us the iterations */
+	iterations = 1000 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+	
+	/* Write out crypto algorithm */
+	salgo = gcry_cipher_algo_name (calgo);
+	g_return_val_if_fail (salgo, FALSE);
+	gkr_buffer_add_string (output, salgo);
+	
+	/* Write out the hash algorithm */
+	salgo = gcry_md_algo_name (halgo);
+	g_return_val_if_fail (halgo, FALSE);
+	gkr_buffer_add_string (output, salgo);
+	
+	/* Write out the iterations */
+	gkr_buffer_add_uint32 (output, iterations);
+	
+	/* And write out the salt */
+	gkr_buffer_add_byte_array (output, salt, sizeof (salt));
+	
+	/* Okay now use the above info to create our cipher context */
+	if (!create_cipher (login, calgo, halgo, salt, sizeof (salt), iterations, &cipher))
+		return FALSE;
+	
+	/* Significant block sizes */
+	n_block = gcry_cipher_get_algo_blklen (calgo);
+	g_return_val_if_fail (n_block, FALSE);
+	
+	/* Pad the buffer to a multiple of block length */
+	while (input->len % n_block != 0)
+		gkr_buffer_add_byte (input, 0);
+	
+	/* Now reserve space for it in the output block, and encrypt */
+	dest = gkr_buffer_add_byte_array_empty (output, input->len);
+	g_return_val_if_fail (dest, FALSE);
+
+	gcry = gcry_cipher_encrypt (cipher, dest, input->len, input->buf, input->len);
+	g_return_val_if_fail (!gcry, FALSE);
+	
+	gcry_cipher_close (cipher);
+
+	return TRUE;
+}
+
+static gboolean
+decrypt_buffer (GkrBuffer *input, gsize *offset, GckLogin *login, GkrBuffer *output)
+{
+	gcry_cipher_hd_t cipher;
+	gcry_error_t gcry;
+	const guchar *salt, *data;
+	gsize n_block, n_salt, n_data;
+	guint32 iterations;
+	int calgo, halgo;
+	gchar *salgo;
+
+	g_assert (input);
+	g_assert (output);
+	g_assert (offset);
+	g_assert (login);
+
+	/* Read in and interpret the cipher algorithm */
+	if (!gkr_buffer_get_string (input, *offset, offset, &salgo, NULL))
+		return FALSE;
+	calgo = gcry_cipher_map_name (salgo); 
+	if (!calgo) {
+		g_warning ("unsupported crypto algorithm: %s", salgo);
+		g_free (salgo);
+		return FALSE;
+	}
+	g_free (salgo);
+		
+	/* Read in and interpret the hash algorithm */
+	if (!gkr_buffer_get_string (input, *offset, offset, &salgo, NULL))
+		return FALSE;
+	halgo = gcry_md_map_name (salgo);
+	if (!halgo) {
+		g_warning ("unsupported crypto algorithm: %s", salgo);
+		g_free (salgo);
+		return FALSE;
+	}
+	g_free (salgo);
+		
+	/* Read in the iterations, salt, and encrypted data */
+	if (!gkr_buffer_get_uint32 (input, *offset, offset, &iterations) ||
+	    !gkr_buffer_get_byte_array (input, *offset, offset, &salt, &n_salt) ||
+	    !gkr_buffer_get_byte_array (input, *offset, offset, &data, &n_data))
+		return FALSE;
+
+	/* Significant block sizes */
+	n_block = gcry_cipher_get_algo_blklen (calgo);
+	g_return_val_if_fail (n_block, FALSE);
+	
+	/* Make sure the encrypted data is of a good length */
+	if (n_data % n_block != 0) {
+		g_warning ("encrypted data in file store is of an invalid length for algorithm");
+		return FALSE;
+	}
+
+	/* Create the cipher context */
+	if (!create_cipher (login, calgo, halgo, salt, n_salt, iterations, &cipher))
+		return FALSE;
+
+	/* Now reserve space for it in the output block, and encrypt */
+	gkr_buffer_reset (output);
+	gkr_buffer_resize (output, n_data);
+
+	gcry = gcry_cipher_decrypt (cipher, output->buf, output->len, data, n_data);
+	g_return_val_if_fail (!gcry, FALSE);
+	
+	gcry_cipher_close (cipher);
+
+	return TRUE;
+}
+
+/* ----------------------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static GckDataResult
+update_entries_from_block (GckDataFile *self, guint section, GHashTable *entries, 
+                           GkrBuffer *buffer, gsize *offset)
+{
+	GHashTable *attributes;
+	const gchar *identifier;
+	CK_ATTRIBUTE_PTR at;
+	CK_ATTRIBUTE attr;
+	gpointer key, value;
+	guint32 n_entries, i;
+	guint32 n_attrs, j;
+	gchar *str;
+	guint sect;
+	const guchar *data;
+	gsize n_data;
+	guint64 type;
+	
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (entries);
+	g_assert (buffer);
+	g_assert (offset);
+	
+	/* The number of entries */
+	if (!gkr_buffer_get_uint32 (buffer, *offset, offset, &n_entries))
+		return GCK_DATA_FAILURE;
+
+	for (i = 0; i < n_entries; ++i) {
+		
+		/* The attributes */
+		if (!gkr_buffer_get_string (buffer, *offset, offset, &str, (GkrBufferAllocator)g_realloc))
+			return GCK_DATA_FAILURE;
+		
+		/* Make sure we have this one */
+		sect = GPOINTER_TO_UINT (g_hash_table_lookup (self->identifiers, str));
+		if (sect != section) {
+			g_message ("data file entry in wrong section: %s", identifier);
+			g_free (str);
+			return GCK_DATA_FAILURE;
+		}
+		
+		/* Lookup or create a new table for it */
+		if (!g_hash_table_lookup_extended (entries, str, &key, &value)) {
+			value = attributes_new ();
+			key = g_strdup (str);
+			g_hash_table_replace (entries, key, value);
+		}
+		
+		g_free (str);
+		identifier = key;
+		attributes = value;
+		
+		if (!gkr_buffer_get_uint32 (buffer, *offset, offset, &n_attrs))
+			return GCK_DATA_FAILURE;
+			
+		for (j = 0; j < n_attrs; ++j) {
+			if (!gkr_buffer_get_uint64 (buffer, *offset, offset, &type) ||
+			    !gkr_buffer_get_byte_array (buffer, *offset, offset, &data, &n_data))
+				return GCK_DATA_FAILURE;
+				
+			attr.type = type;
+			attr.pValue = (CK_VOID_PTR)data;
+			attr.ulValueLen = n_data;
+				
+			at = g_hash_table_lookup (attributes, &attr.type);
+			if (at != NULL && gck_attribute_equal (&attr, at))
+				continue;
+				
+			at = attribute_dup (&attr);
+			g_hash_table_replace (attributes, &(at->type), at);
+			
+			/* Only emit the changed signal if we haven't just added this one */
+			if (!g_hash_table_lookup (self->added, identifier))
+				g_signal_emit (self, signals[ENTRY_CHANGED], 0, identifier, attr.type);
+		}
+	}
+
+	return GCK_DATA_SUCCESS;
+}
+
+static GckDataResult
+update_from_public_block (GckDataFile *self, GkrBuffer *buffer)
+{
+	gsize offset = 0;
+
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (buffer);
+	
+	self->sections |= GCK_DATA_FILE_SECTION_PUBLIC;
+
+	/* Validate the buffer hash, failure in this case is corruption */
+	if (!validate_buffer (buffer, &offset)) 
+		return GCK_DATA_FAILURE;
+	
+	return update_entries_from_block (self, GCK_DATA_FILE_SECTION_PUBLIC, 
+	                                  self->publics, buffer, &offset);
+}
+
+static GckDataResult
+update_from_private_block (GckDataFile *self, GkrBuffer *buffer, GckLogin *login)
+{
+	GkrBuffer custom;
+	GckDataResult res;
+	const gchar *password;
+	gsize n_password;
+	gsize offset;
+
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (buffer);
+
+	self->sections |= GCK_DATA_FILE_SECTION_PRIVATE;
+
+	/* Skip private blocks when not unlocked */
+	if (login == NULL) {
+		if (self->privates)
+			g_hash_table_destroy (self->privates);
+		self->privates = NULL;
+		return GCK_DATA_UNRECOGNIZED;
+	}
+	
+	offset = 0;
+	gkr_buffer_init_full (&custom, 1024, gkr_secure_realloc);
+
+	/* Decrypt the buffer */
+	password = gck_login_get_password (login, &n_password);
+	if (!decrypt_buffer (buffer, &offset, login, &custom)) {
+		gkr_buffer_uninit (&custom);
+		return GCK_DATA_FAILURE;
+	}
+	
+	offset = 0;
+	
+	/* Validate the buffer hash, failure is usually a bad password */
+	if (!validate_buffer (&custom, &offset)) {
+		gkr_buffer_uninit (&custom);
+		return GCK_DATA_LOCKED;
+	}
+
+	/* We're loading privates, so fill that in */
+	if (!self->privates)
+		self->privates = entries_new ();
+
+	res = update_entries_from_block (self, GCK_DATA_FILE_SECTION_PRIVATE,
+	                                 self->privates, &custom, &offset);
+	gkr_buffer_uninit (&custom);
+	return res;
+}
+
+static void
+copy_each_identifier (gpointer key, gpointer value, gpointer data)
+{
+	g_hash_table_insert (data, g_strdup (key), UNUSED_VALUE);
+}
+
+static void
+remove_each_identifier (gpointer key, gpointer value, gpointer data)
+{
+	GckDataFile *self = GCK_DATA_FILE (data);
+	GHashTable *entries;
+	guint section;
+	
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (key);
+
+	if (!gck_data_file_lookup_entry (self, key, &section))
+		g_assert_not_reached ();
+		
+	if (section == GCK_DATA_FILE_SECTION_PRIVATE)
+		entries = self->privates;
+	else
+		entries = self->publics;
+
+	if (!g_hash_table_remove (self->identifiers, key))
+		g_assert_not_reached ();
+	if (entries != NULL && !g_hash_table_remove (entries, key))
+		g_return_if_reached ();
+	
+	g_signal_emit (self, signals[ENTRY_REMOVED], 0, key);
+}
+
+static GckDataResult
+update_from_index_block (GckDataFile *self, GkrBuffer *buffer)
+{
+	gchar *identifier;
+	gsize offset;
+	guint section;
+	guint count, i;
+	guint value;
+	
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (buffer);
+	g_assert (offset);
+	
+	offset = 0;
+	
+	/* The number of entries */
+	if (!gkr_buffer_get_uint32 (buffer, offset, &offset, &count))
+		return FALSE;
+
+	for (i = 0; i < count; ++i) {
+		
+		/* The identifier */
+		if (!gkr_buffer_get_string (buffer, offset, &offset, &identifier, (GkrBufferAllocator)g_realloc))
+			break;
+		
+		/* The section */
+		if (!gkr_buffer_get_uint32 (buffer, offset, &offset, &value)) {
+			g_free (identifier);
+			break;
+		}
+		
+		/* Lookup the section it's currently in */
+		section = GPOINTER_TO_UINT (g_hash_table_lookup (self->identifiers, identifier));
+		if (section == 0 || section != value) 
+			g_hash_table_replace (self->added, g_strdup (identifier), UNUSED_VALUE);
+
+		section = value;
+		g_hash_table_replace (self->identifiers, identifier, GUINT_TO_POINTER (section));
+		
+		/* Track that we've seen this identifier */
+		g_hash_table_remove (self->checks, identifier);
+	}
+	
+	/* Completed reading all */
+	if (i == count) 
+		return GCK_DATA_SUCCESS;
+		
+	/* Failed for some reason, data is bad */
+	return GCK_DATA_FAILURE;
+}
+
+static GckDataResult
+update_from_any_block (guint block, GkrBuffer *buffer, GckLogin *login, gpointer user_data)
+{
+	UnknownBlock *unknown;
+	GckDataFile *self;
+	GckDataResult res;
+	
+	g_assert (GCK_IS_DATA_FILE (user_data));
+	self = GCK_DATA_FILE (user_data);
+	
+	switch (block) {
+	case FILE_BLOCK_INDEX:
+		res = update_from_index_block (self, buffer);
+		break;
+	case FILE_BLOCK_PRIVATE:
+		res = update_from_private_block (self, buffer, login);
+		break;
+	case FILE_BLOCK_PUBLIC:
+		res = update_from_public_block (self, buffer);
+		break;
+	default:
+		res = GCK_DATA_UNRECOGNIZED;
+		break;
+	};
+	
+	/* If unrecognized data block, then stash as unknown */
+	if (res == GCK_DATA_UNRECOGNIZED) {
+		unknown = g_slice_new0 (UnknownBlock);
+		unknown->type = block;
+		gkr_buffer_init_full (&unknown->buffer, buffer->len, PUBLIC_ALLOC);
+		gkr_buffer_append (&unknown->buffer, buffer->buf, buffer->len);
+		self->unknowns = g_list_prepend (self->unknowns, unknown);
+		res = GCK_DATA_SUCCESS;
+	}
+	
+	return res;
+}
+
+static void
+write_each_attribute (gpointer key, gpointer value, gpointer data)
+{
+	CK_ATTRIBUTE_PTR attr = value;
+	GkrBuffer *buffer = data;
+	gkr_buffer_add_uint64 (buffer, attr->type);
+	g_assert (attr->ulValueLen != (gulong)-1);
+	gkr_buffer_add_byte_array (buffer, attr->pValue, attr->ulValueLen);
+}
+
+static void
+write_each_entry (gpointer key, gpointer value, gpointer data)
+{
+	GkrBuffer *buffer = data;
+	const gchar *unique = key;
+	GHashTable *attributes = value;
+	
+	gkr_buffer_add_string (buffer, unique);
+	gkr_buffer_add_uint32 (buffer, g_hash_table_size (attributes));
+	g_hash_table_foreach (attributes, write_each_attribute, buffer);
+}
+
+static GckDataResult
+write_entries_to_block (GckDataFile *self, GHashTable *entries, GkrBuffer *buffer)
+{
+	gsize offset;
+	
+	g_assert (GCK_DATA_FILE (self));
+	g_assert (entries);
+	g_assert (buffer);
+	
+	/* Reserve space for the length */
+	offset = buffer->len;
+	gkr_buffer_add_uint32 (buffer, 0);
+	
+	/* The number of attributes we'll be encountering */
+	gkr_buffer_add_uint32 (buffer, g_hash_table_size (entries));
+	
+	/* Fill in the attributes */
+	g_hash_table_foreach (entries, write_each_entry, buffer);
+	
+	g_return_val_if_fail (!gkr_buffer_has_error (buffer), GCK_DATA_FAILURE);
+	
+	/* Fill in the length */
+	gkr_buffer_set_uint32 (buffer, offset, buffer->len);
+	
+	/* Hash the entire dealio */
+	if (!hash_buffer (buffer)) 
+		return GCK_DATA_FAILURE;
+
+	return GCK_DATA_SUCCESS;
+}
+
+static GckDataResult
+write_private_to_block (GckDataFile *self, GkrBuffer *buffer, GckLogin *login)
+{
+	GkrBuffer secure;
+	GckDataResult res;
+
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (buffer);
+
+	if (login == NULL) {
+		/* Must lock the private data in some way */
+		if (self->privates && g_hash_table_size (self->privates))
+			return GCK_DATA_LOCKED;
+		
+		/* Not storing privates */
+		else
+			return GCK_DATA_UNRECOGNIZED;
+	} else {
+		/* We didn't load the privates, can't store them back */
+		if (self->privates == NULL)
+			return GCK_DATA_LOCKED;
+	}
+
+	gkr_buffer_init_full (&secure, 1024, PRIVATE_ALLOC);
+
+	res = write_entries_to_block (self, self->privates, &secure);
+	if (res == GCK_DATA_SUCCESS)
+		res = encrypt_buffer (&secure, login, buffer);
+	
+	gkr_buffer_uninit (&secure);
+	return res;
+}
+
+static GckDataResult
+write_public_to_block (GckDataFile *self, GkrBuffer *buffer)
+{
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (buffer);
+	
+	return write_entries_to_block (self, self->publics, buffer);
+}
+
+static void
+write_each_index_identifier (gpointer key, gpointer value, gpointer data)
+{
+	gkr_buffer_add_string (data, key);
+	gkr_buffer_add_uint32 (data, GPOINTER_TO_UINT (value));
+}
+
+static GckDataResult
+write_index_to_block (GckDataFile *self, GkrBuffer *buffer)
+{
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (buffer);
+	
+	/* The number of entries */
+	gkr_buffer_add_uint32 (buffer, g_hash_table_size (self->identifiers));
+	
+	/* Now write out all the entries */
+	g_hash_table_foreach (self->identifiers, write_each_index_identifier, buffer);
+	
+	return gkr_buffer_has_error (buffer) ? GCK_DATA_FAILURE : GCK_DATA_SUCCESS;
+}
+
+static GckDataResult
+identifier_to_attributes (GckDataFile *self, const gchar *identifier, GHashTable **attributes)
+{
+	GHashTable *entries;
+	gpointer value;
+	guint section;
+	
+	g_assert (GCK_IS_DATA_FILE (self));
+	g_assert (identifier);
+	g_assert (attributes);
+	
+	if (!g_hash_table_lookup_extended (self->identifiers, identifier, NULL, &value))
+		return GCK_DATA_UNRECOGNIZED;
+	
+	section = GPOINTER_TO_UINT (value);
+	if (section == GCK_DATA_FILE_SECTION_PRIVATE)
+		entries = self->privates;
+	else
+		entries = self->publics;
+	
+	if (entries == NULL)
+		return GCK_DATA_LOCKED;
+	
+	*attributes = g_hash_table_lookup (entries, identifier);
+	g_return_val_if_fail (*attributes, GCK_DATA_UNRECOGNIZED);
+	
+	return GCK_DATA_SUCCESS;
+}
+
+static void
+free_unknown_block_list (GList *list)
+{
+	UnknownBlock *unknown;
+	GList *l;
+	
+	for (l = list; l; l = g_list_next (l)) {
+		unknown = l->data;
+		g_assert (unknown);
+		gkr_buffer_uninit (&unknown->buffer);
+		g_slice_free (UnknownBlock, unknown);
+	}
+	
+	g_list_free (list);
+}
+
+static gint
+sort_unknowns_by_type (gconstpointer a, gconstpointer b)
+{
+	const UnknownBlock *ua = a;
+	const UnknownBlock *ub = b;
+	
+	g_assert (ua);
+	g_assert (ub);
+	
+	if (ua->type == ub->type)
+		return 0;
+	
+	return ua->type > ub->type ? 1 : -1;
+}
+
+typedef struct _ForeachArgs {
+	GckDataFile *self;
+	GckDataFileFunc func;
+	gpointer user_data;
+} ForeachArgs;
+
+static void
+foreach_identifier (gpointer key, gpointer value, gpointer data)
+{
+	ForeachArgs *args = data;
+	g_assert (GCK_IS_DATA_FILE (args->self));
+	(args->func) (args->self, key, args->user_data);
+}
+
+static void
+emit_each_added_identifier (gpointer key, gpointer value, gpointer data)
+{
+	GckDataFile *self = GCK_DATA_FILE (data);
+	g_signal_emit (self, signals[ENTRY_ADDED], 0, key);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static void
+gck_data_file_init (GckDataFile *self)
+{
+	self->identifiers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); 
+	self->publics = entries_new ();
+	self->privates = entries_new ();
+	
+	self->unknowns = NULL;
+	
+	self->added = NULL;
+	self->checks = NULL;
+}
+
+static void
+gck_data_file_finalize (GObject *obj)
+{
+	GckDataFile *self = GCK_DATA_FILE (obj);
+
+	g_assert (self->identifiers);
+	g_hash_table_destroy (self->identifiers);
+	self->identifiers = NULL;
+	
+	g_assert (self->added == NULL);
+	g_assert (self->checks == NULL);
+	
+	g_assert (self->publics);
+	g_hash_table_destroy (self->publics);
+	self->publics = NULL;
+	
+	if (self->privates)
+		g_hash_table_destroy (self->privates);
+	self->privates = NULL;
+	
+	free_unknown_block_list (self->unknowns);
+	self->unknowns = NULL;
+	
+	G_OBJECT_CLASS (gck_data_file_parent_class)->finalize (obj);
+}
+
+static void
+gck_data_file_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                                GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_data_file_get_property (GObject *obj, guint prop_id, GValue *value, 
+                                GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_data_file_class_init (GckDataFileClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    
+	gobject_class->finalize = gck_data_file_finalize;
+	gobject_class->set_property = gck_data_file_set_property;
+	gobject_class->get_property = gck_data_file_get_property;
+
+	signals[ENTRY_ADDED] = g_signal_new ("entry-added", GCK_TYPE_DATA_FILE, 
+	                                G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GckDataFileClass, entry_added),
+	                                NULL, NULL, g_cclosure_marshal_VOID__STRING, 
+	                                G_TYPE_NONE, 1, G_TYPE_STRING);
+
+	signals[ENTRY_CHANGED] = g_signal_new ("entry-changed", GCK_TYPE_DATA_FILE, 
+	                                G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GckDataFileClass, entry_changed),
+	                                NULL, NULL, gck_marshal_VOID__STRING_ULONG, 
+	                                G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_ULONG);
+
+	signals[ENTRY_REMOVED] = g_signal_new ("entry-removed", GCK_TYPE_DATA_FILE, 
+	                                G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GckDataFileClass, entry_removed),
+	                                NULL, NULL, g_cclosure_marshal_VOID__STRING, 
+	                                G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+
+GckDataFile*
+gck_data_file_new (void)
+{
+	return g_object_new (GCK_TYPE_DATA_FILE, NULL);
+}
+
+GckDataResult
+gck_data_file_read_fd (GckDataFile *self, int fd, GckLogin *login)
+{
+	GckDataResult res;
+	
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+
+	/* Reads are not reentrant for a single data file */
+	g_return_val_if_fail (self->checks == NULL, GCK_DATA_FAILURE);
+	g_return_val_if_fail (self->added == NULL, GCK_DATA_FAILURE);
+
+	self->sections = 0;
+
+	/* Free all the old unknowns */
+	free_unknown_block_list (self->unknowns);
+	self->unknowns = NULL;
+
+	/* Setup a hash table to monitor the actual data read */
+	self->checks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	self->added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	g_hash_table_foreach (self->identifiers, copy_each_identifier, self->checks);
+	
+	res = parse_file_blocks (fd, update_from_any_block, login, self);
+	
+	/* 
+	 * Always need to fire off added signals for all identifiers 
+	 * whether (partially) successful or not.
+	 */
+
+	g_hash_table_foreach (self->added, emit_each_added_identifier, self);
+
+	if (res == GCK_DATA_SUCCESS) {
+		
+		/* Our last read was a success, can write */
+		self->incomplete = FALSE;
+
+		/* Remove the ones we didn't see */
+		g_hash_table_foreach (self->checks, remove_each_identifier, self);
+
+		/* 
+		 * There's a special where we've read a file without a private section.
+		 * We should be ready to accept privates (and then lock them next 
+		 * time around). 
+		 */
+		
+		if (self->privates == NULL && !(self->sections & GCK_DATA_FILE_SECTION_PRIVATE))
+			self->privates = entries_new ();
+		
+	/* Note that our last read failed */
+	} else {
+		self->incomplete = TRUE;
+	}
+	
+	g_hash_table_destroy (self->checks);
+	g_hash_table_destroy (self->added);
+	self->checks = self->added = NULL;
+	
+	return res;
+}
+
+GckDataResult
+gck_data_file_write_fd (GckDataFile *self, int fd, GckLogin *login)
+{
+	guint types[3] = { FILE_BLOCK_INDEX, FILE_BLOCK_PRIVATE, FILE_BLOCK_PUBLIC };
+	GList *unknowns, *unk;
+	UnknownBlock *block;
+	GckDataResult res;
+	GkrBuffer buffer;
+	guint type;
+	gint i;
+	
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+	g_return_val_if_fail (!self->incomplete, GCK_DATA_FAILURE);
+
+	/* Write out the header */
+	if (!write_all_bytes (fd, FILE_HEADER, FILE_HEADER_LEN))
+		return GCK_DATA_FAILURE;
+
+	unknowns = g_list_copy (self->unknowns);
+	unknowns = g_list_sort (unknowns, sort_unknowns_by_type);
+	gkr_buffer_init_full (&buffer, 8192, PUBLIC_ALLOC);
+
+	/* 
+	 * All blocks are written in sorted order by their block
+	 * type. This includes unknown blocks. 
+	 */
+
+	unk = unknowns;
+	res = GCK_DATA_SUCCESS;
+	
+	for (i = 0; i < G_N_ELEMENTS(types); ++i) {
+		type = types[i];
+		
+		/* Write out all the unknowns before this block */
+		while (unk != NULL && res == GCK_DATA_SUCCESS) {
+			block = (UnknownBlock*)unk->data;
+			if (block->type > type)
+				break;
+			res = write_file_block (fd, block->type, &block->buffer);
+			unk = g_list_next (unk);
+		}
+		
+		if (res != GCK_DATA_SUCCESS)
+			break;
+		
+		/* Prepare the block of this type */
+		gkr_buffer_reset (&buffer);
+		switch (type) {
+		case FILE_BLOCK_INDEX:
+			res = write_index_to_block (self, &buffer);
+			break;
+		case FILE_BLOCK_PRIVATE:
+			res = write_private_to_block (self, &buffer, login);
+			break;
+		case FILE_BLOCK_PUBLIC:
+			res = write_public_to_block (self, &buffer);
+			break;
+		}
+		
+		/* Write it out, if we got anything */
+		if (res == GCK_DATA_SUCCESS)
+			res = write_file_block (fd, type, &buffer);
+		else if (res == GCK_DATA_UNRECOGNIZED)
+			res = GCK_DATA_SUCCESS;
+		
+		if (res != GCK_DATA_SUCCESS)
+			break;
+	}
+	
+	/* Write out all remaining unknowns */
+	while (unk != NULL && res == GCK_DATA_SUCCESS) {
+		block = (UnknownBlock*)unk->data;
+		res = write_file_block (fd, block->type, &block->buffer);
+		unk = g_list_next (unk);
+	}
+	
+	g_list_free (unknowns);
+	gkr_buffer_uninit (&buffer);
+	return res;
+}
+
+gboolean
+gck_data_file_lookup_entry (GckDataFile *self, const gchar *identifier, guint *section)
+{
+	gpointer value;
+	
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), FALSE);
+	g_return_val_if_fail (identifier, FALSE);
+	
+	if (!g_hash_table_lookup_extended (self->identifiers, identifier, NULL, &value))
+		return FALSE;
+	
+	if (section != NULL)
+		*section = GPOINTER_TO_UINT (value);
+	
+	return TRUE;
+}
+
+void
+gck_data_file_foreach_entry (GckDataFile *self, GckDataFileFunc func, gpointer user_data)
+{
+	ForeachArgs args = { self, func, user_data };
+	
+	g_return_if_fail (GCK_IS_DATA_FILE (self));
+	g_return_if_fail (func);
+	
+	g_hash_table_foreach (self->identifiers, foreach_identifier, &args);
+}
+
+GckDataResult
+gck_data_file_unique_entry (GckDataFile *self, gchar **identifier)
+{
+	gchar *base, *ext;
+	guint seed = 1;
+	
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+	g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+
+	/* Check if original is unique */
+	if (*identifier != NULL) {
+		if (!gck_data_file_lookup_entry (self, *identifier, NULL))
+			return GCK_DATA_SUCCESS;
+	}
+
+	if (*identifier == NULL)
+		*identifier = g_strdup_printf ("object-%08x", ABS (g_random_int ()));
+	
+	/* Take ownership of the identifier, and out an extension */
+	base = *identifier;
+	*identifier = NULL;
+	ext = strrchr (base, '.');
+	if (ext != NULL)
+		*(ext++) = '\0';
+	
+	for (seed = 0; TRUE; ++seed) {
+		*identifier = g_strdup_printf ("%s-%d%s%s", base, seed, ext ? "." : "", ext ? ext : "");
+		if (!gck_data_file_lookup_entry (self, *identifier, NULL))
+			break;
+		
+		if (seed < 1000000) {
+			g_warning ("couldn't find a unique identifier in a %d tries", seed);
+			g_free (base);
+			return GCK_DATA_FAILURE;
+		}
+		
+		g_free (*identifier);
+		*identifier = NULL;
+	}
+	
+	g_free (base);
+	return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_create_entry (GckDataFile *self, const gchar *identifier, guint section)
+{
+	GHashTable *attributes;
+	GHashTable *entries;
+	
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+	g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+	
+	if (section == GCK_DATA_FILE_SECTION_PRIVATE) {
+		if (!self->privates)
+			return GCK_DATA_LOCKED;
+		entries = self->privates;
+	} else {
+		entries = self->publics;
+	}
+
+	/* Make sure it's not already here */
+	g_return_val_if_fail (g_hash_table_lookup (entries, identifier) == NULL, GCK_DATA_FAILURE);
+	
+	/* Add the new entry to the appropriate table */
+	attributes = attributes_new ();
+	g_hash_table_replace (entries, g_strdup (identifier), attributes);
+	g_hash_table_replace (self->identifiers, g_strdup (identifier), GUINT_TO_POINTER (section));
+
+	g_signal_emit (self, signals[ENTRY_ADDED], 0, identifier);
+	return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_destroy_entry (GckDataFile *self, const gchar *identifier)
+{
+	GHashTable *entries;
+	guint section;
+	
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+	g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+
+	if (!gck_data_file_lookup_entry (self, identifier, &section))
+		return GCK_DATA_UNRECOGNIZED;
+		
+	if (section == GCK_DATA_FILE_SECTION_PRIVATE) {
+		if (!self->privates)
+			return GCK_DATA_LOCKED;
+		entries = self->privates;
+	} else {
+		entries = self->publics;
+	}
+
+	if (!g_hash_table_remove (self->identifiers, identifier))
+		g_return_val_if_reached (GCK_DATA_UNRECOGNIZED);
+	if (!g_hash_table_remove (entries, identifier))
+		g_return_val_if_reached (GCK_DATA_UNRECOGNIZED);
+	
+	g_signal_emit (self, signals[ENTRY_REMOVED], 0, identifier);
+	return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_write_value (GckDataFile *self, const gchar *identifier,
+                           gulong type, gconstpointer value, gsize n_value)
+{
+	GHashTable *attributes;
+	CK_ATTRIBUTE_PTR at;
+	CK_ATTRIBUTE attr;
+	GckDataResult res;
+
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+	g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+	g_return_val_if_fail (value || !n_value, GCK_DATA_FAILURE);
+
+	/* Find the right set of attributes */
+	res = identifier_to_attributes (self, identifier, &attributes);
+	if (res != GCK_DATA_SUCCESS)
+		return res;
+
+	attr.type = type;
+	attr.pValue = (void*)value;
+	attr.ulValueLen = n_value;
+	
+	at = g_hash_table_lookup (attributes, &type);
+	if (at != NULL && gck_attribute_equal (at, &attr))
+		return GCK_DATA_SUCCESS;
+	
+	at = attribute_dup (&attr);
+	g_hash_table_replace (attributes, &(at->type), at);
+	
+	g_signal_emit (self, signals[ENTRY_CHANGED], 0, identifier, type);
+	return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_read_value (GckDataFile *self, const gchar *identifier,
+                          gulong type, gconstpointer *value, gsize *n_value)
+{
+	CK_ATTRIBUTE_PTR attr;
+	GHashTable *attributes;
+	GckDataResult res;
+
+	g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+	g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+	g_return_val_if_fail (value, GCK_DATA_FAILURE);
+	g_return_val_if_fail (n_value, GCK_DATA_FAILURE);
+
+	/* Find the right set of attributes */
+	res = identifier_to_attributes (self, identifier, &attributes);
+	if (res != GCK_DATA_SUCCESS)
+		return res;
+	
+	attr = g_hash_table_lookup (attributes, &type);
+	if (attr == NULL)
+		return GCK_DATA_UNRECOGNIZED;
+	
+	g_assert (attr->type == type);
+	*value = attr->pValue;
+	*n_value = attr->ulValueLen;
+	return GCK_DATA_SUCCESS;	
+}
+
+gboolean
+gck_data_file_have_section (GckDataFile *self, guint section)
+{
+	return (self->sections & section) ? TRUE : FALSE;
+}

Added: trunk/pkcs11/gck/gck-data-file.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-data-file.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,104 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_DATA_FILE_H__
+#define __GCK_DATA_FILE_H__
+
+#include <glib-object.h>
+
+#include "gck-data-types.h"
+#include "gck-login.h"
+
+enum {
+	GCK_DATA_FILE_SECTION_PUBLIC  = 0x01,
+	GCK_DATA_FILE_SECTION_PRIVATE = 0x02,
+};
+
+#define GCK_TYPE_DATA_FILE               (gck_data_file_get_type ())
+#define GCK_DATA_FILE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_DATA_FILE, GckDataFile))
+#define GCK_DATA_FILE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_DATA_FILE, GckDataFileClass))
+#define GCK_IS_DATA_FILE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_DATA_FILE))
+#define GCK_IS_DATA_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_DATA_FILE))
+#define GCK_DATA_FILE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_DATA_FILE, GckDataFileClass))
+
+typedef struct _GckDataFile GckDataFile;
+typedef struct _GckDataFileClass GckDataFileClass;
+    
+struct _GckDataFileClass {
+	GObjectClass parent_class;
+	
+	/* signals */
+	
+	void (*entry_added) (GckDataFile *store, const gchar *identifier);
+	
+	void (*entry_changed) (GckDataFile *store, const gchar *identifier, CK_ATTRIBUTE_TYPE type);
+	
+	void (*entry_removed) (GckDataFile *store, const gchar *identifier);
+};
+
+typedef void (*GckDataFileFunc) (GckDataFile *file, const gchar *identifier, gpointer user_data);
+
+GType                     gck_data_file_get_type               (void);
+
+GckDataFile*              gck_data_file_new                    (void);
+
+GckDataResult             gck_data_file_read_fd                (GckDataFile *self,
+                                                                int fd, 
+                                                                GckLogin *login);
+
+GckDataResult             gck_data_file_write_fd               (GckDataFile *self,
+                                                                int fd, 
+                                                                GckLogin *login);
+
+gboolean                  gck_data_file_have_section           (GckDataFile *self,
+                                                                guint section);
+
+gboolean                  gck_data_file_lookup_entry           (GckDataFile *self,
+                                                                const gchar *identifier,
+                                                                guint *section);
+
+void                      gck_data_file_foreach_entry          (GckDataFile *self,
+                                                                GckDataFileFunc func,
+                                                                gpointer user_data);
+
+GckDataResult             gck_data_file_unique_entry           (GckDataFile *self,
+                                                                gchar **identifier);
+
+GckDataResult             gck_data_file_create_entry           (GckDataFile *self, 
+                                                                const gchar *identifier,
+                                                                guint section);
+
+GckDataResult             gck_data_file_destroy_entry          (GckDataFile *self, 
+                                                                const gchar *identifier);
+
+GckDataResult             gck_data_file_write_value            (GckDataFile *self,
+                                                                const gchar *identifier,
+                                                                gulong type,
+                                                                gconstpointer value,
+                                                                gsize n_value);
+
+GckDataResult             gck_data_file_read_value             (GckDataFile *self,
+                                                                const gchar *identifier,
+                                                                gulong type,
+                                                                gconstpointer *value,
+                                                                gsize *n_value);
+     
+#endif /* __GCK_DATA_FILE_H__ */

Modified: trunk/pkcs11/gck/gck-data-openssl.c
==============================================================================
--- trunk/pkcs11/gck/gck-data-openssl.c	(original)
+++ trunk/pkcs11/gck/gck-data-openssl.c	Sat Jan 17 20:17:52 2009
@@ -25,6 +25,7 @@
 
 #include "gck-crypto.h"
 #include "gck-data-openssl.h"
+#include "gck-util.h"
 
 #include <gcrypt.h>
 #include <libtasn1.h>
@@ -133,78 +134,8 @@
 	/* CAMELLIA-256-OFB */
 };
 
-static const char HEXC[] = "0123456789ABCDEF";
-
 /* ------------------------------------------------------------------------- */
 
-static gboolean
-hex_decode (const gchar *data, gsize n_data, 
-            guchar *decoded, gsize *n_decoded)
-{
-	gushort j;
-	gint state = 0;
-	const gchar* pos;
-    
-	g_assert (data);
-	g_assert (decoded);
-	g_assert (n_decoded);
-    
-	g_return_val_if_fail (*n_decoded >= n_data / 2, FALSE);
-	*n_decoded = 0;
-
-	while (n_data > 0) 
-    	{
-    		if (!g_ascii_isspace (*data)) {
-    			
-	        	/* Find the position */
-			pos = strchr (HEXC, g_ascii_toupper (*data));
-			if (pos == 0)
-				break;
-
-			j = pos - HEXC;
-			if(!state) {
-				*decoded = (j & 0xf) << 4;
-				state = 1;
-			} else {      
-				*decoded |= (j & 0xf);
-				(*n_decoded)++;
-				decoded++;
-				state = 0;
-			}
-    		}
-      
-      		++data;
-      		--n_data;
-	}
-  
-  	g_return_val_if_fail (state == 0, FALSE);
-  	
-  	return TRUE;
-}
-
-static gboolean
-hex_encode (const guchar *data, gsize n_data, 
-            gchar *encoded, gsize *n_encoded)
-{
-	guchar j;
-	
-	g_return_val_if_fail (*n_encoded >= n_data * 2 + 1, FALSE);
-	
-	while(n_data > 0) {
-		j = *(data) >> 4 & 0xf;
-		*(encoded++) = HEXC[j];
-    
-		j = *(data++) & 0xf;
-		*(encoded++) = HEXC[j];
-    
-		n_data--;
-	}
-
-	/* Null terminate */
-	*encoded = 0;
-	return TRUE;
-}
-
 int
 gck_data_openssl_parse_algo (const char *name, int *mode)
 {
@@ -255,15 +186,10 @@
 		goto done;
 
 	/* Parse the IV */
-	ivlen = len = gcry_cipher_get_algo_blklen (*algo);
-	*iv = g_malloc (ivlen);
+	ivlen = gcry_cipher_get_algo_blklen (*algo);
 	
-	if (!hex_decode (parts[1], strlen (parts[1]), *iv, &len)) {
-		g_free (*iv);
-		goto done;
-	}
-	
-	if (ivlen != len) {
+	*iv = gck_util_hex_decode (parts[1], strlen(parts[1]), &len);
+	if (!*iv || ivlen != len) {
 		g_free (*iv);
 		goto done;
 	}
@@ -421,13 +347,10 @@
 const gchar*
 gck_data_openssl_prep_dekinfo (GHashTable *headers)
 {
-	gsize ivlen, len, n_encoded;
-	gchar buf[256];
+	gchar *dekinfo, *hex;
+	gsize ivlen;
 	guchar *iv;
-	const gchar *dekinfo;
 	
-	strcpy (buf, "DES-EDE3-CBC,");
-
 	/* Create the iv */
 	ivlen = gcry_cipher_get_algo_blklen (GCRY_CIPHER_3DES);
 	g_return_val_if_fail (ivlen, NULL);
@@ -435,13 +358,11 @@
 	gcry_create_nonce (iv, ivlen);
 	
 	/* And encode it into the string */
-	len = strlen (buf);
-	g_return_val_if_fail (sizeof (buf) - len > ivlen * 2, NULL);
-	n_encoded = (ivlen * 2) + 1;
-	if (!hex_encode (iv, ivlen, buf + len, &n_encoded))
-		g_return_val_if_reached (NULL);
+	hex = gck_util_hex_encode (iv, ivlen);
+	g_return_val_if_fail (hex, NULL);
+	dekinfo = g_strdup_printf ("DES-EDE3-CBC,%s", hex);
+	g_free (hex);
 
-	dekinfo = g_strdup (buf);
 	g_hash_table_insert (headers, g_strdup ("DEK-Info"), (void*)dekinfo);
 	g_hash_table_insert (headers, g_strdup ("Proc-Type"), g_strdup ("4,ENCRYPTED"));
 	

Added: trunk/pkcs11/gck/gck-login.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-login.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,153 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-login.h"
+
+#include "common/gkr-secure-memory.h"
+
+#include <string.h>
+
+struct _GckLogin {
+	GObject parent;
+	gchar *password;
+	gsize n_password;
+};
+
+G_DEFINE_TYPE (GckLogin, gck_login, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static void
+gck_login_init (GckLogin *self)
+{
+
+}
+
+static void
+gck_login_dispose (GObject *obj)
+{
+	GckLogin *self = GCK_LOGIN (obj);
+	
+	gkr_secure_strfree (self->password);
+	self->password = NULL;
+	self->n_password = 0;
+    
+	G_OBJECT_CLASS (gck_login_parent_class)->dispose (obj);
+}
+
+static void
+gck_login_finalize (GObject *obj)
+{
+	GckLogin *self = GCK_LOGIN (obj);
+	
+	g_assert (!self->password);
+	g_assert (!self->n_password);
+
+	G_OBJECT_CLASS (gck_login_parent_class)->finalize (obj);
+}
+
+static void
+gck_login_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                        GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_login_get_property (GObject *obj, guint prop_id, GValue *value, 
+                        GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_login_class_init (GckLoginClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    
+	gobject_class->dispose = gck_login_dispose;
+	gobject_class->finalize = gck_login_finalize;
+	gobject_class->set_property = gck_login_set_property;
+	gobject_class->get_property = gck_login_get_property;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+
+GckLogin*
+gck_login_new (CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+{
+	GckLogin *login = g_object_new (GCK_TYPE_LOGIN, NULL);
+	
+	if (pin) {
+		if (n_pin == (CK_ULONG)-1) {
+			login->password = gkr_secure_strdup ((const gchar*)pin);
+			login->n_password = strlen (login->password);
+		} else {
+			login->password = gkr_secure_alloc (n_pin + 1);
+			memcpy (login->password, pin, n_pin);
+			login->n_password = n_pin;
+		}
+	} else {
+		login->password = NULL;
+		login->n_password = 0;
+	}
+	
+	return login;
+}
+
+const gchar*
+gck_login_get_password (GckLogin *self, gsize *n_password)
+{
+	g_return_val_if_fail (GCK_IS_LOGIN (self), NULL);
+	*n_password = self->n_password;
+	return self->password;
+}
+
+gboolean
+gck_login_equals (GckLogin *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+{
+	g_return_val_if_fail (GCK_IS_LOGIN (self), FALSE);
+	
+	if (n_pin == (CK_ULONG)-1 && pin != NULL)
+		n_pin = strlen ((const gchar*)pin);
+	
+	if (n_pin != self->n_password)
+		return FALSE;
+	if (!pin && !self->password)
+		return TRUE;
+	if (!pin || !self->password)
+		return FALSE;
+	return memcmp (pin, self->password, n_pin) == 0;
+}

Added: trunk/pkcs11/gck/gck-login.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-login.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,56 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_LOGIN_H__
+#define __GCK_LOGIN_H__
+
+#include <glib-object.h>
+
+#include "gck-types.h"
+
+#include "pkcs11/pkcs11.h"
+
+#define GCK_TYPE_LOGIN               (gck_login_get_type ())
+#define GCK_LOGIN(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_LOGIN, GckLogin))
+#define GCK_LOGIN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_LOGIN, GckLoginClass))
+#define GCK_IS_LOGIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_LOGIN))
+#define GCK_IS_LOGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_LOGIN))
+#define GCK_LOGIN_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_LOGIN, GckLoginClass))
+
+typedef struct _GckLoginClass GckLoginClass;
+    
+struct _GckLoginClass {
+	GObjectClass parent_class;
+};
+
+GType               gck_login_get_type               (void);
+
+GckLogin*           gck_login_new                    (CK_UTF8CHAR_PTR pin, 
+                                                      CK_ULONG n_pin);
+
+const gchar*        gck_login_get_password           (GckLogin *self, 
+                                                      gsize *n_pin);
+
+gboolean            gck_login_equals                 (GckLogin *self,
+                                                      CK_UTF8CHAR_PTR pin,
+                                                      CK_ULONG n_pin);
+
+#endif /* __GCK_LOGIN_H__ */

Modified: trunk/pkcs11/gck/gck-marshal.list
==============================================================================
--- trunk/pkcs11/gck/gck-marshal.list	(original)
+++ trunk/pkcs11/gck/gck-marshal.list	Sat Jan 17 20:17:52 2009
@@ -1 +1,2 @@
 BOOLEAN:VOID
+VOID:STRING,ULONG

Modified: trunk/pkcs11/gck/gck-module-ep.h
==============================================================================
--- trunk/pkcs11/gck/gck-module-ep.h	(original)
+++ trunk/pkcs11/gck/gck-module-ep.h	Sat Jan 17 20:17:52 2009
@@ -354,18 +354,11 @@
 gck_C_InitPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
 {
 	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
-	GckSession *session;
 	
 	g_static_mutex_lock (&pkcs11_module_mutex);
 	
-		if (pkcs11_module != NULL) {
-			session = gck_module_lookup_session (pkcs11_module, handle);
-			if (session != NULL)
-				rv = gck_session_C_InitPIN (session,
-				                            pin, pin_len);
-			else
-				rv = CKR_SESSION_HANDLE_INVALID;
-		}
+		if (pkcs11_module != NULL)
+			rv = gck_module_C_InitPIN (pkcs11_module, handle, pin, pin_len);
 			
 	g_static_mutex_unlock (&pkcs11_module_mutex);
 
@@ -376,18 +369,11 @@
 gck_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
 {
 	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
-	GckSession *session;
 	
 	g_static_mutex_lock (&pkcs11_module_mutex);
 	
-		if (pkcs11_module != NULL) {
-			session = gck_module_lookup_session (pkcs11_module, handle);
-			if (session != NULL)
-				rv = gck_session_C_SetPIN (session,
-				                           old_pin, old_pin_len, new_pin, new_pin_len);
-			else
-				rv = CKR_SESSION_HANDLE_INVALID;
-		}
+		if (pkcs11_module != NULL)
+			rv = gck_module_C_SetPIN (pkcs11_module, handle, old_pin, old_pin_len, new_pin, new_pin_len);
 			
 	g_static_mutex_unlock (&pkcs11_module_mutex);
 

Modified: trunk/pkcs11/gck/gck-module.c
==============================================================================
--- trunk/pkcs11/gck/gck-module.c	(original)
+++ trunk/pkcs11/gck/gck-module.c	Sat Jan 17 20:17:52 2009
@@ -350,6 +350,13 @@
 }
 
 static CK_RV
+gck_module_real_login_change (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin, 
+                              CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
+{
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
 gck_module_real_login_user (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
 {
 	VirtualSlot *slot;
@@ -514,6 +521,7 @@
 	klass->refresh_token = gck_module_real_refresh_token;
 	klass->store_token_object = gck_module_real_store_token_object;
 	klass->remove_token_object = gck_module_real_remove_token_object;
+	klass->login_change = gck_module_real_login_change;
 	klass->login_user = gck_module_real_login_user;
 	klass->logout_user = gck_module_real_logout_user;
 	
@@ -567,6 +575,15 @@
 }
 
 CK_RV
+gck_module_login_change (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin, 
+                         CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
+{
+	g_return_val_if_fail (GCK_IS_MODULE (self), CKR_GENERAL_ERROR);
+	g_assert (GCK_MODULE_GET_CLASS (self)->login_change);
+	return GCK_MODULE_GET_CLASS (self)->login_change (self, slot_id, old_pin, n_old_pin, new_pin, n_new_pin);
+}
+
+CK_RV
 gck_module_login_user (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
 {
 	g_return_val_if_fail (GCK_IS_MODULE (self), CKR_GENERAL_ERROR);
@@ -946,6 +963,35 @@
 }
 
 CK_RV
+gck_module_C_InitPIN (GckModule* self, CK_SESSION_HANDLE handle, 
+                      CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
+{
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+gck_module_C_SetPIN (GckModule* self, CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin,
+                     CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
+{
+	GckSession *session;
+	VirtualSlot *slot;
+	CK_SLOT_ID slot_id;
+	
+	g_return_val_if_fail (GCK_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
+	
+	session = gck_module_lookup_session (self, handle);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	/* Calculate the virtual slot */
+	slot_id = gck_session_get_slot_id (session);
+	slot = lookup_virtual_slot (self, slot_id);
+	g_return_val_if_fail (slot, CKR_GENERAL_ERROR);
+
+	return gck_module_login_change (self, slot_id, old_pin, old_pin_len, new_pin, new_pin_len);
+}
+
+CK_RV
 gck_module_C_Login (GckModule *self, CK_SESSION_HANDLE handle, CK_USER_TYPE user_type,
                     CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
 {

Modified: trunk/pkcs11/gck/gck-module.h
==============================================================================
--- trunk/pkcs11/gck/gck-module.h	(original)
+++ trunk/pkcs11/gck/gck-module.h	Sat Jan 17 20:17:52 2009
@@ -64,7 +64,11 @@
 	void (*store_token_object) (GckModule *self, GckTransaction *transaction, GckObject *object);
 	
 	void (*remove_token_object) (GckModule *self, GckTransaction *transaction, GckObject *object);
-	
+
+	CK_RV (*login_change) (GckModule *self, CK_SLOT_ID slot_id, 
+	                       CK_UTF8CHAR_PTR old_pin, CK_ULONG n_old_pin,
+	                       CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin);
+
 	CK_RV (*login_user) (GckModule *self, CK_SLOT_ID slot_id, 
 	                     CK_UTF8CHAR_PTR pin, CK_ULONG n_pin);
 	
@@ -103,6 +107,13 @@
 GckSession*            gck_module_lookup_session                  (GckModule *self,
                                                                    CK_SESSION_HANDLE handle);
 
+CK_RV                  gck_module_login_change                    (GckModule *self, 
+                                                                   CK_SLOT_ID slot_id, 
+                                                                   CK_UTF8CHAR_PTR old_pin, 
+                                                                   CK_ULONG n_old_pin,
+                                                                   CK_UTF8CHAR_PTR new_pin, 
+                                                                   CK_ULONG n_new_pin);
+
 CK_RV                  gck_module_login_user                      (GckModule *self,
                                                                    CK_SLOT_ID slot_id,
                                                                    CK_UTF8CHAR_PTR pin,
@@ -173,6 +184,18 @@
 CK_RV                  gck_module_C_CloseAllSessions              (GckModule *self, 
                                                                    CK_SLOT_ID id);
 
+CK_RV                  gck_module_C_InitPIN                       (GckModule* self, 
+                                                                   CK_SESSION_HANDLE session,
+                                                                   CK_UTF8CHAR_PTR pin,
+                                                                   CK_ULONG pin_len);
+
+CK_RV                  gck_module_C_SetPIN                        (GckModule* self,
+                                                                   CK_SESSION_HANDLE session,
+                                                                   CK_UTF8CHAR_PTR old_pin,
+                                                                   CK_ULONG old_pin_len, 
+                                                                   CK_UTF8CHAR_PTR new_pin, 
+                                                                   CK_ULONG new_pin_len);
+
 CK_RV                  gck_module_C_Login                         (GckModule *self, 
                                                                    CK_SESSION_HANDLE session, 
                                                                    CK_USER_TYPE user_type,

Added: trunk/pkcs11/gck/gck-serializable.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-serializable.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,79 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-serializable.h"
+
+static void
+gck_serializable_base_init (gpointer gobject_class)
+{
+	static gboolean initialized = FALSE;
+	if (!initialized) {
+		/* Add properties and signals to the interface */
+		
+		
+		initialized = TRUE;
+	}
+}
+
+GType
+gck_serializable_get_type (void)
+{
+	static volatile gsize type_id__volatile = 0;
+	
+	if (g_once_init_enter (&type_id__volatile)) {
+		static const GTypeInfo info = {
+			sizeof (GckSerializableIface),
+			gck_serializable_base_init,               /* base init */
+			NULL,             /* base finalize */
+			NULL,             /* class_init */
+			NULL,             /* class finalize */
+			NULL,             /* class data */
+			0,
+			0,                /* n_preallocs */
+			NULL,             /* instance init */
+		};
+		
+		GType type_id = g_type_register_static (G_TYPE_INTERFACE, "GckSerializableIface", &info, 0);
+		g_type_interface_add_prerequisite (type_id, G_TYPE_OBJECT);
+		
+		g_once_init_leave (&type_id__volatile, type_id);
+	}
+	
+	return type_id__volatile;
+}
+
+gboolean
+gck_serializable_load (GckSerializable *self, GckLogin *login, const guchar *data, gsize n_data)
+{
+	g_return_val_if_fail (GCK_IS_SERIALIZABLE (self), FALSE);
+	g_return_val_if_fail (GCK_SERIALIZABLE_GET_INTERFACE (self)->load, FALSE);
+	return GCK_SERIALIZABLE_GET_INTERFACE (self)->load (self, login, data, n_data);
+}
+
+gboolean
+gck_serializable_save (GckSerializable *self, GckLogin *login, guchar **data, gsize *n_data)
+{
+	g_return_val_if_fail (GCK_IS_SERIALIZABLE (self), FALSE);
+	g_return_val_if_fail (GCK_SERIALIZABLE_GET_INTERFACE (self)->save, FALSE);
+	return GCK_SERIALIZABLE_GET_INTERFACE (self)->save (self, login, data, n_data);
+}

Added: trunk/pkcs11/gck/gck-serializable.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-serializable.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,64 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_SERIALIZABLE_H__
+#define __GCK_SERIALIZABLE_H__
+
+#include <glib-object.h>
+
+#include "gck-types.h"
+
+G_BEGIN_DECLS
+
+#define GCK_TYPE_SERIALIZABLE                 (gck_serializable_get_type())
+#define GCK_SERIALIZABLE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SERIALIZABLE, GckSerializable))
+#define GCK_IS_SERIALIZABLE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_SERIALIZABLE))
+#define GCK_SERIALIZABLE_GET_INTERFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCK_TYPE_SERIALIZABLE, GckSerializableIface))
+
+typedef struct _GckSerializable      GckSerializable;
+typedef struct _GckSerializableIface GckSerializableIface;
+
+struct _GckSerializableIface {
+	GTypeInterface parent;
+	
+	const gchar *extension;
+
+	gboolean (*load) (GckSerializable *self, GckLogin *login, const guchar *data, gsize n_data);
+	
+	gboolean (*save) (GckSerializable *self, GckLogin *login, guchar **data, gsize *n_data);
+};
+
+GType                  gck_serializable_get_type                          (void) G_GNUC_CONST;
+
+gboolean               gck_serializable_load                              (GckSerializable *self,
+                                                                           GckLogin *login,
+                                                                           const guchar *data,
+                                                                           gsize n_data);
+
+gboolean                gck_serializable_save                             (GckSerializable *self,
+                                                                           GckLogin *login,
+                                                                           guchar** data,
+                                                                           gsize *n_data);
+
+G_END_DECLS
+
+#endif /* __GCK_SERIALIZABLE_H__ */
+

Modified: trunk/pkcs11/gck/gck-session.c
==============================================================================
--- trunk/pkcs11/gck/gck-session.c	(original)
+++ trunk/pkcs11/gck/gck-session.c	Sat Jan 17 20:17:52 2009
@@ -692,23 +692,6 @@
 }
 
 CK_RV
-gck_session_C_InitPIN (GckSession* self, CK_UTF8CHAR_PTR pin,
-                       CK_ULONG pin_len)
-{
-	/* We don't support this stuff. We don't support 'SO' logins. */
-	return CKR_USER_NOT_LOGGED_IN;	
-}
-
-CK_RV
-gck_session_C_SetPIN (GckSession* self, CK_UTF8CHAR_PTR old_pin,
-                      CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
-{
-	/* TODO: We may support this in the future. */
-	return CKR_FUNCTION_NOT_SUPPORTED;
-
-}
-
-CK_RV
 gck_session_C_GetOperationState (GckSession* self, CK_BYTE_PTR operation_state,
                                  CK_ULONG_PTR operation_state_len)
 {

Modified: trunk/pkcs11/gck/gck-session.h
==============================================================================
--- trunk/pkcs11/gck/gck-session.h	(original)
+++ trunk/pkcs11/gck/gck-session.h	Sat Jan 17 20:17:52 2009
@@ -97,16 +97,6 @@
 CK_RV                    gck_session_C_GetSessionInfo                   (GckSession* self, 
                                                                          CK_SESSION_INFO_PTR info);
 
-CK_RV                    gck_session_C_InitPIN                          (GckSession* self, 
-                                                                         CK_UTF8CHAR_PTR pin,
-                                                                         CK_ULONG pin_len);
-
-CK_RV                    gck_session_C_SetPIN                           (GckSession* self, 
-                                                                         CK_UTF8CHAR_PTR old_pin,
-                                                                         CK_ULONG old_pin_len, 
-                                                                         CK_UTF8CHAR_PTR new_pin, 
-                                                                         CK_ULONG new_pin_len);
-
 CK_RV                    gck_session_C_GetOperationState                (GckSession* self, 
                                                                          CK_BYTE_PTR operation_state,
                                                                          CK_ULONG_PTR operation_state_len);

Modified: trunk/pkcs11/gck/gck-store.h
==============================================================================
--- trunk/pkcs11/gck/gck-store.h	(original)
+++ trunk/pkcs11/gck/gck-store.h	Sat Jan 17 20:17:52 2009
@@ -54,6 +54,7 @@
 	/* Virtual methods */
     
 	CK_RV (*read_value) (GckStore *self, GckObject *object, CK_ATTRIBUTE_PTR attr);
+	
 	void (*write_value) (GckStore *self, GckTransaction *transaction, GckObject *object, CK_ATTRIBUTE_PTR attr); 
 };
 

Modified: trunk/pkcs11/gck/gck-transaction.c
==============================================================================
--- trunk/pkcs11/gck/gck-transaction.c	(original)
+++ trunk/pkcs11/gck/gck-transaction.c	Sat Jan 17 20:17:52 2009
@@ -24,6 +24,12 @@
 #include "gck-marshal.h"
 #include "gck-transaction.h"
 
+#include <glib/gstdio.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
 enum {
 	PROP_0,
 	PROP_COMPLETED,
@@ -63,7 +69,6 @@
 {
 	g_assert (complete);
 	g_assert (complete->func);
-	g_assert (complete->object);
 	
 	return (complete->func) (transaction, complete->object, complete->user_data);
 }
@@ -72,8 +77,8 @@
 complete_destroy (Complete *complete)
 {
 	g_assert (complete->func);
-	g_assert (G_IS_OBJECT (complete->object));
-	g_object_unref (complete->object);
+	if (complete->object)
+		g_object_unref (complete->object);
 	g_slice_free (Complete, complete);
 }
 
@@ -92,6 +97,94 @@
 	return TRUE;
 }
 
+static gboolean
+complete_new_file (GckTransaction *self, GObject *unused, gpointer user_data)
+{
+	gchar *path = user_data;
+	gboolean ret = TRUE;
+	
+	if (gck_transaction_get_failed (self)) {
+		if (g_unlink (path) < 0) {
+			g_warning ("couldn't delete aborted file, data may be lost: %s: %s",
+			           path, g_strerror (errno));
+			ret = FALSE;
+		}
+	}
+	
+	g_free (path);
+	return ret;
+}
+
+static gboolean
+begin_new_file (GckTransaction *self, const gchar *filename)
+{
+	g_assert (GCK_IS_TRANSACTION (self));
+	g_assert (!gck_transaction_get_failed (self));
+	g_assert (filename);
+	
+	gck_transaction_add (self, NULL, complete_new_file, g_strdup (filename));
+	return TRUE;
+}
+
+static gboolean
+complete_link_temporary (GckTransaction *self, GObject *unused, gpointer user_data)
+{
+	gchar *path = user_data;
+	gboolean ret = TRUE;
+	gchar *original;
+	gchar *ext;
+	
+	if (gck_transaction_get_failed (self)) {
+		
+		/* Figure out the original file name */
+		original = g_strdup (path);
+		ext = strrchr (original, '.');
+		g_return_val_if_fail (ext, FALSE);
+		*ext = '\0';
+		
+		/* Now rename us back */
+		if (!g_rename (path, original) == -1) {
+			g_warning ("couldn't restore original file, data may be lost: %s: %s", 
+			           original, g_strerror (errno));
+			ret = FALSE;
+		}
+		
+		g_free (original);
+	}
+	
+	g_free (path);
+	return ret;
+}
+
+static gboolean
+begin_link_temporary (GckTransaction *self, const gchar *filename)
+{
+	gchar *result;
+	
+	g_assert (GCK_IS_TRANSACTION (self));
+	g_assert (!gck_transaction_get_failed (self));
+	g_assert (filename);
+	
+	for (;;) {
+		/* Try to link to random temporary file names */
+		result = g_strdup_printf ("%s.temp-%d", filename, g_random_int_range (0, G_MAXINT));
+		if (link (filename, result) == 0) {
+			gck_transaction_add (self, NULL, complete_link_temporary, result);
+			return TRUE;
+		}
+		
+		g_free (result);
+		
+		if (errno != EEXIST) {
+			g_warning ("couldn't create temporary file for: %s: %s", filename, g_strerror (errno));
+			gck_transaction_fail (self, CKR_DEVICE_ERROR);
+			return FALSE;
+		}
+	}
+	
+	g_assert_not_reached ();
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT 
  */
@@ -224,12 +317,12 @@
 	Complete *complete;
 	
 	g_return_if_fail (GCK_IS_TRANSACTION (self));
-	g_return_if_fail (G_IS_OBJECT (object));
 	g_return_if_fail (func);
 	
 	complete = g_slice_new0 (Complete);
 	complete->func = func;
-	complete->object = g_object_ref (object);
+	if (object)
+		complete->object = g_object_ref (object);
 	complete->user_data = user_data;
 	
 	self->completes = g_list_prepend (self->completes, complete);
@@ -289,3 +382,52 @@
 	g_return_val_if_fail (GCK_IS_TRANSACTION (self), FALSE);
 	return self->result;
 }
+
+void
+gck_transaction_write_file (GckTransaction *self, const gchar *filename,
+                            const guchar *data, gsize n_data)
+{
+	GError *error = NULL;
+	
+	g_return_if_fail (GCK_IS_TRANSACTION (self));
+	g_return_if_fail (filename);
+	g_return_if_fail (data);
+	g_return_if_fail (!gck_transaction_get_failed (self));
+	
+	/* Open and lock the file */
+	
+	if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+		if (!begin_new_file (self, filename))
+			return;
+	} else {
+		if (!begin_link_temporary (self, filename))
+			return;
+	}
+
+	if (!g_file_set_contents (filename, (const gchar*)data, n_data, &error)) {
+		g_warning ("couldn't write to file: %s: %s", filename, 
+		           error && error->message ? error->message : "");
+		gck_transaction_fail (self, CKR_DEVICE_ERROR);
+	}
+}
+
+void
+gck_transaction_remove_file (GckTransaction *self, const gchar *filename)
+{
+	g_return_if_fail (GCK_IS_TRANSACTION (self));
+	g_return_if_fail (filename);
+	g_return_if_fail (!gck_transaction_get_failed (self));
+
+	/* Already gone? Job accomplished */
+	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+		return;
+
+	if (!begin_link_temporary (self, filename))
+		return;
+	
+	/* If failure, temporary will automatically be removed */
+	if (g_unlink (filename) < 0) {
+		g_warning ("couldn't remove file: %s: %s", filename, g_strerror (errno));
+		gck_transaction_fail (self, CKR_DEVICE_ERROR);
+	}
+}

Modified: trunk/pkcs11/gck/gck-transaction.h
==============================================================================
--- trunk/pkcs11/gck/gck-transaction.h	(original)
+++ trunk/pkcs11/gck/gck-transaction.h	Sat Jan 17 20:17:52 2009
@@ -69,4 +69,12 @@
 
 gboolean                    gck_transaction_get_completed          (GckTransaction *self);
 
+void                        gck_transaction_write_file             (GckTransaction *self,
+                                                                    const gchar *filename,
+                                                                    const guchar *data,
+                                                                    gsize n_data);
+
+void                        gck_transaction_remove_file            (GckTransaction *self,
+                                                                    const gchar *filename);
+
 #endif /* __GCK_TRANSACTION_H__ */

Modified: trunk/pkcs11/gck/gck-types.h
==============================================================================
--- trunk/pkcs11/gck/gck-types.h	(original)
+++ trunk/pkcs11/gck/gck-types.h	Sat Jan 17 20:17:52 2009
@@ -26,6 +26,7 @@
 typedef struct _GckCertificateKey GckCertificateKey;
 typedef struct _GckKey GckKey;
 typedef struct _GckFactoryInfo GckFactoryInfo;
+typedef struct _GckLogin GckLogin;
 typedef struct _GckManager GckManager;
 typedef struct _GckModule GckModule;
 typedef struct _GckObject GckObject;

Modified: trunk/pkcs11/gck/gck-util.c
==============================================================================
--- trunk/pkcs11/gck/gck-util.c	(original)
+++ trunk/pkcs11/gck/gck-util.c	Sat Jan 17 20:17:52 2009
@@ -29,6 +29,8 @@
 /* Only access using atomic operations */
 static gint next_handle = 0x00000010;
 
+static const char HEXC[] = "0123456789ABCDEF";
+
 gulong*
 gck_util_ulong_alloc (gulong value)
 {
@@ -87,3 +89,79 @@
 {
 	return (CK_ULONG)g_atomic_int_exchange_and_add (&next_handle, 1);
 }
+
+guchar*
+gck_util_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded)
+{
+	guchar *result;
+	guchar *decoded;
+	gushort j;
+	gint state = 0;
+	const gchar* pos;
+    
+	g_return_val_if_fail (data || !n_data, NULL);
+	g_return_val_if_fail (n_decoded, NULL);
+	
+	if (n_data == -1)
+		n_data = strlen (data);
+
+	decoded = result = g_malloc0 ((n_data / 2) + 1);
+	*n_decoded = 0;
+
+	while (n_data > 0) {
+    		if (!g_ascii_isspace (*data)) {
+    			
+	        	/* Find the position */
+			pos = strchr (HEXC, g_ascii_toupper (*data));
+			if (pos == 0)
+				break;
+
+			j = pos - HEXC;
+			if(!state) {
+				*decoded = (j & 0xf) << 4;
+				state = 1;
+			} else {      
+				*decoded |= (j & 0xf);
+				(*n_decoded)++;
+				decoded++;
+				state = 0;
+			}
+    		}
+      
+      		++data;
+      		--n_data;
+	}
+
+	/* Parsing error */
+	if (state != 0) {
+		g_free (result);
+		result = NULL;
+	}
+
+	return result;
+}
+
+gchar* 
+gck_util_hex_encode (const guchar *data, gsize n_data)
+{
+	gchar *result, *encoded;
+	guchar j;
+	
+	g_return_val_if_fail (data || !n_data, NULL);
+	
+	encoded = result = g_malloc0 (n_data * 2 + 1);
+	
+	while(n_data > 0) {
+		j = *(data) >> 4 & 0xf;
+		*(encoded++) = HEXC[j];
+    
+		j = *(data++) & 0xf;
+		*(encoded++) = HEXC[j];
+    
+		n_data--;
+	}
+
+	/* Make sure still null terminated */
+	g_assert (encoded[n_data * 2] == 0);
+	return result;
+}

Modified: trunk/pkcs11/gck/gck-util.h
==============================================================================
--- trunk/pkcs11/gck/gck-util.h	(original)
+++ trunk/pkcs11/gck/gck-util.h	Sat Jan 17 20:17:52 2009
@@ -42,9 +42,16 @@
                                                                    gconstpointer input,
                                                                    gsize n_input);
 
-CK_RV                 gck_attribute_set_mpi                            (CK_ATTRIBUTE_PTR attr, 
+CK_RV                 gck_attribute_set_mpi                       (CK_ATTRIBUTE_PTR attr, 
                                                                    gcry_mpi_t mpi);
 
 CK_ULONG              gck_util_next_handle                        (void);
 
+guchar*               gck_util_hex_decode                         (const gchar *data, 
+                                                                   gssize n_data, 
+                                                                   gsize *n_decoded);
+
+gchar*                gck_util_hex_encode                         (const guchar *data, 
+                                                                   gsize n_data);
+
 #endif /* GCKUTIL_H_ */

Modified: trunk/pkcs11/gck/tests/Makefile.am
==============================================================================
--- trunk/pkcs11/gck/tests/Makefile.am	(original)
+++ trunk/pkcs11/gck/tests/Makefile.am	Sat Jan 17 20:17:52 2009
@@ -7,6 +7,7 @@
 	
 # Test files should be listed in order they need to run
 UNIT_AUTO = \
+	unit-test-util.c \
 	unit-test-crypto.c \
 	unit-test-data-asn1.c \
 	unit-test-data-der.c \
@@ -14,7 +15,8 @@
 	unit-test-transaction.c \
 	unit-test-store.c \
 	unit-test-memory-store.c \
-	unit-test-file-store.c \
+	unit-test-login.c \
+	unit-test-data-file.c \
 	unit-test-file-tracker.c \
 	$(BUILT_SOURCES)
 

Added: trunk/pkcs11/gck/tests/test-data/data-file-private.store
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/data-file-public.store
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-MD5-DES.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-DES.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-dsa.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-encrypted-pkcs5.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des3.key
==============================================================================
Binary file. No diff available.

Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8.key
==============================================================================
Binary file. No diff available.

Modified: trunk/pkcs11/gck/tests/unit-test-crypto.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-crypto.c	(original)
+++ trunk/pkcs11/gck/tests/unit-test-crypto.c	Sat Jan 17 20:17:52 2009
@@ -31,9 +31,45 @@
 
 #include <gcrypt.h>
 
+#define TEST_RSA \
+"(private-key (rsa " \
+"(n  #00B78758D55EBFFAB61D07D0DC49B5309A6F1DA2AE51C275DFC2370959BB81AC0C39093B1C618E396161A0DECEB8768D0FFB14F197B96C3DA14190EE0F20D51315#)" \
+"(e #010001#)" \
+"(d #108BCAC5FDD35812981E6EC5957D98E2AB76E4064C47B861D27C2CC322C50792313C852B4164A035B42D261F1A09F9FFE8F477F9F78FF2EABBDA6BA875C671D7#)" \
+"(p #00C357F11B19A18C66573D25D1E466D9AB8BCDDCDFE0B2E80BD46712C4BEC18EB7#)" \
+"(q #00F0843B90A60EF7034CA4BE80414ED9497CABCC685143B388013FF989CBB0E093#)" \
+"(u #12F2555F52EB56329A991CF0404B51C68AC921AD370A797860F550415FF987BD#)" \
+"))"
+
+#define TEST_DSA \
+"(private-key (dsa " \
+"  (p #0090EC0B60735839C754EAF8F64BB03FC35398D69772BFAE540079DEA2D3A61FAFFB27630A038A01A3D0CD62A10745A574A27ECB462F4F0885B79C61BBE954A60A29668AD54BBA5C07A72FD8B1105249670B339DF2C59E64A47064EFCF0B7236C5C72CD55CEB32917430BEC9A003D4E484FBAA84D79571B38D6B5AC95BB73E3F7B#)" \
+"  (q #00FA214A1385C21BFEBAADAB240A2430C607D56271#)" \
+"  (g #2DE05751F5DAEE97F3D43C54595A3E94A080728F0C66C98AEBED5762F6AB155802D8359EAD1DE1EC36A459FBEEEA48E59B9E6A8CB4F5295936B3CC881A5D957C7339175E2CFFE0F30D3711E430DB6648C2EB474AA10A4A3297450531FF2C7C6951220C9D446B6B6B0F00262E1EBEB3CC861476AA518CC555C9ABF9E5F39023FC#)" \
+"  (y #54734451DB79D4EEDF0BBCEBD43BB6CBB7B8584603B957080075DD318EB5B0266D4B20DC5EFF376BDFC4EA2983B1F7F02A39ED4C619ED68712729FFF3B7C696ADD1B6D748F56A4B4BEC5C4385E528423A3B88AE65E6D5500F97839E7A486255982189C3B4FA8D94338C76F0E5CAFC9A30A1ED728BB9F2091D594E3250A09EA00#)" \
+"  (x #00876F84F709D51108DFB0CBFA1F1C569C09C413EC#)))"
+
+gcry_sexp_t rsakey = NULL;
+gcry_sexp_t dsakey = NULL;
+
 DEFINE_SETUP(crypto_setup)
 {
+	gcry_error_t gcry;
+	
 	gck_crypto_initialize ();
+	
+	gcry = gcry_sexp_new (&rsakey, TEST_RSA, strlen (TEST_RSA), 1);
+	g_return_if_fail (gcry == 0);
+	gcry = gcry_sexp_new (&dsakey, TEST_DSA, strlen (TEST_DSA), 1);
+	g_return_if_fail (gcry == 0);
+}
+
+DEFINE_TEARDOWN(crypto_setup)
+{
+	gcry_sexp_release (rsakey);
+	rsakey = NULL;
+	gcry_sexp_release (dsakey);
+	dsakey = NULL;
 }
 
 const static struct {
@@ -220,78 +256,60 @@
 	}
 }
 
-#if 0
-#define TEST_KEY \
-"(private-key (rsa " \
-"(n  #00B78758D55EBFFAB61D07D0DC49B5309A6F1DA2AE51C275DFC2370959BB81AC0C39093B1C618E396161A0DECEB8768D0FFB14F197B96C3DA14190EE0F20D51315#)" \
-"(e #010001#)" \
-"(d #108BCAC5FDD35812981E6EC5957D98E2AB76E4064C47B861D27C2CC322C50792313C852B4164A035B42D261F1A09F9FFE8F477F9F78FF2EABBDA6BA875C671D7#)" \
-"(p #00C357F11B19A18C66573D25D1E466D9AB8BCDDCDFE0B2E80BD46712C4BEC18EB7#)" \
-"(q #00F0843B90A60EF7034CA4BE80414ED9497CABCC685143B388013FF989CBB0E093#)" \
-"(u #12F2555F52EB56329A991CF0404B51C68AC921AD370A797860F550415FF987BD#)" \
-"))"
-
-gcry_sexp_t thekey = NULL;
-
-void unit_test_parse_key (CuTest *cu)
+DEFINE_TEST(parse_key)
 {
 	gcry_sexp_t sexp = NULL;
-	gcry_error_t gcry;
 	gcry_mpi_t mpi = NULL;
 	gboolean ret;
 	gboolean is_priv = FALSE;
 	int algorithm = 0;
 	
-	gcry = gcry_sexp_new (&sexp, TEST_KEY, strlen (TEST_KEY), 1);
-	g_return_if_fail (gcry == 0);
-	
 	/* Get the private key out */
-	thekey = gkr_crypto_sexp_get_child (sexp, "private-key", NULL);
-	CuAssert (cu, "couldn't extract private key", sexp != NULL);
-	
-	ret = gkr_crypto_skey_parse (thekey, &algorithm, &is_priv, &sexp);
-	CuAssert (cu, "couldn't parse rsa key", ret);
-	CuAssert (cu, "parsed bad algorithm", algorithm == GCRY_PK_RSA);
-	CuAssert (cu, "not a private-key", is_priv == TRUE);
-	CuAssert (cu, "didn't get numbers", sexp != NULL);
-	
-	ret = gkr_crypto_sexp_extract_mpi (sexp, &mpi, "p", NULL);
-	CuAssert (cu, "couldn't extract mpi from key", ret);
-	CuAssert (cu, "no mpi returned from extract", mpi != NULL);
-}
-
-void unit_test_make_keyid (CuTest *cu)
-{
-	guchar hash[20];
-	gkrid id;
-	const guchar *p;
-	gsize n;
-	
-	p = gcry_pk_get_keygrip (thekey, hash);
-	g_return_if_fail (p == hash);
-	
-	id = gkr_crypto_skey_make_id (thekey);
-	CuAssert (cu, "null returned as key id", id != NULL);
-	
-	p = gkr_id_get_raw (id, &n);
-	CuAssert (cu, "key id is of wrong length", n == sizeof (hash));
-	CuAssert (cu, "key grip doesn't match key id", memcmp (hash, p, n) == 0);	
+	ret = gck_crypto_sexp_parse_key (rsakey, &algorithm, &is_priv, &sexp);
+	g_assert (ret);
+	g_assert (algorithm == GCRY_PK_RSA);
+	g_assert (is_priv == TRUE);
+	g_assert (sexp != NULL);
+	
+	ret = gck_crypto_sexp_extract_mpi (rsakey, &mpi, "p", NULL);
+	g_assert (ret);
+	g_assert (mpi != NULL);
 }
 
-void unit_test_key_to_public (CuTest *cu)
+DEFINE_TEST(sexp_key_to_public)
 {
 	gcry_sexp_t pubkey = NULL;
+	guchar id1[20], id2[20];
 	gboolean ret;
-	gkrid u1, u2;
+	guchar *p;
+	
+	/* RSA */
+	ret = gck_crypto_sexp_key_to_public (rsakey, &pubkey);
+	g_assert (ret);
+	g_assert (pubkey != NULL);
 	
-	ret = gkr_crypto_skey_private_to_public (thekey, &pubkey);
-	CuAssert (cu, "couldn't make public key", ret);
-	CuAssert (cu, "returned null public key", pubkey != NULL);
-	
-	u1 = gkr_crypto_skey_make_id (thekey);
-	u2 = gkr_crypto_skey_make_id (pubkey);
-	CuAssert (cu, "public and private keys are not equivalent", 
-	          gkr_id_equals (u1, u2)); 
-}	
+	p = gcry_pk_get_keygrip (rsakey, id1);
+	g_return_if_fail (p == id1);
+	p = gcry_pk_get_keygrip (pubkey, id2);
+	g_return_if_fail (p == id2);
+
+	g_assert (memcmp (id1, id2, sizeof (id1)) == 0);
+	
+	gcry_sexp_release (pubkey);
+
 
-#endif
+	/* DSA */
+	ret = gck_crypto_sexp_key_to_public (dsakey, &pubkey);
+	g_assert (ret);
+	g_assert (pubkey != NULL);
+	
+	p = gcry_pk_get_keygrip (dsakey, id1);
+	g_return_if_fail (p == id1);
+	p = gcry_pk_get_keygrip (pubkey, id2);
+	g_return_if_fail (p == id2);
+
+	g_assert (memcmp (id1, id2, sizeof (id1)) == 0);
+	
+	gcry_sexp_release (pubkey);
+
+}

Modified: trunk/pkcs11/gck/tests/unit-test-data-asn1.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-data-asn1.c	(original)
+++ trunk/pkcs11/gck/tests/unit-test-data-asn1.c	Sat Jan 17 20:17:52 2009
@@ -42,21 +42,19 @@
 static ASN1_TYPE asn1_test = NULL;
 
 static ASN1_TYPE asn1_cert = NULL;
-static gchar *data_cert = NULL;
+static guchar *data_cert = NULL;
 static gsize n_data_cert = 0;
 
 DEFINE_SETUP(asn1_tree)
 {
 	ASN1_TYPE pkix;
-	gboolean ret;
 	
 	int res = asn1_array2tree (test_asn1_tab, &asn1_test, NULL);
 	g_assert (res == ASN1_SUCCESS);
 
 	/* -------- */
 	
-	ret = g_file_get_contents ("test-data/test-certificate-1.der", (gchar**)&data_cert, &n_data_cert, NULL);
-	g_assert ("couldn't read in file: test-data/test-certificate-1.der" && ret);
+	data_cert = test_read_testdata ("test-certificate-1.der", &n_data_cert);
 
 	/* We'll be catching this error later */
 	pkix = gck_data_asn1_get_pkix_asn1type ();
@@ -301,6 +299,14 @@
 	g_assert (check == oid);
 	g_assert_cmpstr (g_quark_to_string (oid), ==, "SOME DATA");
 	
+	/* Write a different OID */ 
+	if (!gck_data_asn1_write_oid (asn, "data", g_quark_from_static_string ("ANOTHER")))
+		g_assert_not_reached ();
+	
+	oid = gck_data_asn1_read_oid (asn, "data");
+	g_assert (oid);
+	g_assert_cmpstr (g_quark_to_string (oid), ==, "ANOTHER");
+	
 	asn1_delete_structure (&asn);
 }
 

Modified: trunk/pkcs11/gck/tests/unit-test-data-der.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-data-der.c	(original)
+++ trunk/pkcs11/gck/tests/unit-test-data-der.c	Sat Jan 17 20:17:52 2009
@@ -104,11 +104,7 @@
 
 DEFINE_SETUP(preload)
 {
-	gboolean ret;
-	
-	ret = g_file_get_contents ("test-data/test-certificate-1.der", (gchar**)&certificate_data, &n_certificate_data, NULL);
-	g_assert ("couldn't read in file: test-data/test-certificate-1.der" && ret);
-	
+	certificate_data = test_read_testdata ("test-certificate-1.der", &n_certificate_data);	
 	certificate = gck_data_asn1_decode ("PKIX1.Certificate", certificate_data, n_certificate_data);
 	g_assert (certificate);
 }
@@ -256,6 +252,18 @@
 	asn1_delete_structure (&asn);
 }
 
+DEFINE_TEST(write_certificate)
+{
+	guchar *data;
+	gsize n_data;
+	
+	data = gck_data_der_write_certificate (certificate, &n_data);
+	g_assert (data);
+	g_assert (n_data == n_certificate_data);
+	g_assert (memcmp (data, certificate_data, n_data) == 0);
+	g_free (data);
+}
+
 DEFINE_TEST(read_basic_constraints)
 {
 	const guchar *extension;
@@ -272,3 +280,142 @@
 	g_assert (is_ca == TRUE);
 	g_assert (path_len == -1);
 }
+
+DEFINE_TEST(read_all_pkcs8)
+{
+	gcry_sexp_t sexp;
+	GckDataResult res;
+	GDir *dir;
+	const gchar *name;
+	guchar *data;
+	gsize n_data;
+	
+	dir = g_dir_open ("test-data", 0, NULL);
+	g_assert (dir);
+	
+	for(;;) {
+		name = g_dir_read_name (dir);
+		if (!name)
+			break;
+		
+		if (!g_pattern_match_simple ("der-pkcs8-*", name))
+			continue;
+		
+		data = test_read_testdata (name, &n_data);
+		res = gck_data_der_read_private_pkcs8 (data, n_data, "booo", 4, &sexp);
+		g_assert (res == GCK_DATA_SUCCESS);
+		
+		g_assert (gck_crypto_sexp_parse_key (sexp, NULL, NULL, NULL));
+		gcry_sexp_release (sexp);
+		g_free (data);
+	}
+	
+	g_dir_close (dir);
+}
+
+DEFINE_TEST(read_pkcs8_bad_password)
+{
+	gcry_sexp_t sexp;
+	GckDataResult res;
+	guchar *data;
+	gsize n_data;
+	
+	data = test_read_testdata ("der-pkcs8-encrypted-pkcs5.key", &n_data);
+	res = gck_data_der_read_private_pkcs8 (data, n_data, "wrong password", 4, &sexp);
+	g_assert (res == GCK_DATA_LOCKED);
+	
+	g_free (data);
+}
+
+DEFINE_TEST(write_pkcs8_plain)
+{
+	gcry_sexp_t sexp, check;
+	gcry_error_t gcry;
+	GckDataResult res;
+	guchar *data;
+	gsize n_data;
+	
+	/* RSA */
+	
+	gcry = gcry_sexp_sscan (&sexp, NULL, rsaprv, strlen (rsaprv));
+	g_return_if_fail (gcry == 0);
+	
+	data = gck_data_der_write_private_pkcs8_plain (sexp, &n_data);
+	g_assert (data);
+	g_assert (n_data);
+	
+	res = gck_data_der_read_private_pkcs8_plain (data, n_data, &check);
+	g_free (data);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (check);
+	
+	g_assert (compare_keys (sexp, check));
+	gcry_sexp_release (sexp);
+	gcry_sexp_release (check);
+	
+	
+	/* DSA */
+
+	gcry = gcry_sexp_sscan (&sexp, NULL, dsaprv, strlen (dsaprv));
+	g_return_if_fail (gcry == 0);
+	
+	data = gck_data_der_write_private_pkcs8_plain (sexp, &n_data);
+	g_assert (data);
+	g_assert (n_data);
+	
+	res = gck_data_der_read_private_pkcs8_plain (data, n_data, &check);
+	g_free (data);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (check);
+	
+	g_assert (compare_keys (sexp, check));
+	gcry_sexp_release (sexp);
+	gcry_sexp_release (check);
+}
+
+
+DEFINE_TEST(write_pkcs8_encrypted)
+{
+	gcry_sexp_t sexp, check;
+	gcry_error_t gcry;
+	GckDataResult res;
+	guchar *data;
+	gsize n_data;
+	
+	/* RSA */
+	
+	gcry = gcry_sexp_sscan (&sexp, NULL, rsaprv, strlen (rsaprv));
+	g_return_if_fail (gcry == 0);
+	
+	data = gck_data_der_write_private_pkcs8_crypted (sexp, "testo", 5, &n_data);
+	g_assert (data);
+	g_assert (n_data);
+	
+	res = gck_data_der_read_private_pkcs8_crypted (data, n_data, "testo", 5, &check);
+	g_free (data);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (check);
+	
+	g_assert (compare_keys (sexp, check));
+	gcry_sexp_release (sexp);
+	gcry_sexp_release (check);
+	
+	
+	/* DSA */
+
+	gcry = gcry_sexp_sscan (&sexp, NULL, dsaprv, strlen (dsaprv));
+	g_return_if_fail (gcry == 0);
+	
+	data = gck_data_der_write_private_pkcs8_crypted (sexp, "testo", 5, &n_data);
+	g_assert (data);
+	g_assert (n_data);
+	
+	res = gck_data_der_read_private_pkcs8_crypted (data, n_data, "testo", 5, &check);
+	g_free (data);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (check);
+	
+	g_assert (compare_keys (sexp, check));
+	gcry_sexp_release (sexp);
+	gcry_sexp_release (check);
+}

Copied: trunk/pkcs11/gck/tests/unit-test-data-file.c (from r1452, /trunk/pkcs11/gck/tests/unit-test-file-store.c)
==============================================================================
--- /trunk/pkcs11/gck/tests/unit-test-file-store.c	(original)
+++ trunk/pkcs11/gck/tests/unit-test-data-file.c	Sat Jan 17 20:17:52 2009
@@ -23,366 +23,565 @@
 
 #include "run-auto-test.h"
 
-#include "gck/gck-file-store.h"
+#include "gck/gck-data-file.h"
 #include "gck/gck-object.h"
-#include "gck/gck-transaction.h"
 
-/* Both point to the same thing */
-static GckStore *store = NULL;
-static GckFileStore *file_store = NULL;
-static GckTransaction *transaction = NULL;
-static GckObject *object = NULL;
-static GckObject *prv_object = NULL;
-static gchar *test_filename = NULL;
+#include <glib/gstdio.h>
 
-static void
-copy_file (const gchar *from, const gchar *to)
-{
-	gchar *contents;
-	gsize length;
-	gboolean ret;
-	
-	ret = g_file_get_contents (from, &contents, &length, NULL);
-	g_assert (ret == TRUE);
-	ret = g_file_set_contents (to, contents, length, NULL);
-	g_assert (ret == TRUE);
-	g_free (contents);
-}
+#include <fcntl.h>
+
+/* Both point to the same thing */
+static GckDataFile *data_file = NULL;
+static gchar *public_filename = NULL;
+static gchar *private_filename = NULL;
+static gchar *write_filename = NULL;
+static int write_fd = -1;
+static int public_fd = -1;
+static int private_fd = -1;
+static GckLogin *login = NULL;
 
 DEFINE_SETUP(file_store)
 {
-	CK_ATTRIBUTE attr;
-	CK_ULONG twentyfour = 24;
-	
-	test_filename = test_build_filename ("unit-test-file-store");
+	public_filename = g_build_filename ("test-data", "data-file-public.store", NULL); 
+	private_filename = g_build_filename ("test-data", "data-file-private.store", NULL); 
+	write_filename = test_build_filename ("unit-test-file.store");
 
-	copy_file ("./test-data/test-file-store.store", test_filename);
-	file_store = gck_file_store_new (test_filename);
-	store = GCK_STORE (file_store);
+	data_file = gck_data_file_new ();
 
-	attr.type = CKA_LABEL;
-	attr.pValue = "label";
-	attr.ulValueLen = 5;
+	public_fd = g_open (public_filename, O_RDONLY, 0);
+	g_assert (public_fd != -1);
 	
-	gck_store_register_schema (store, &attr, NULL, 0);
-	g_assert (gck_store_lookup_schema (store, CKA_LABEL, NULL));
-
-	attr.type = CKA_VALUE;
-	attr.pValue = NULL;
-	attr.ulValueLen = 0;
+	private_fd = g_open (private_filename, O_RDONLY, 0);
+	g_assert (private_fd != -1);
+	
+	write_fd = g_open (write_filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+	g_assert (write_fd != -1);
 	
-	gck_store_register_schema (store, &attr, NULL, GCK_STORE_IS_SENSITIVE);
+	login = gck_login_new ((CK_UTF8CHAR_PTR)"booo", 4);
+}
+
+DEFINE_TEARDOWN(file_store)
+{
+	g_free (public_filename);
+	g_free (private_filename);
+	g_free (write_filename);
 	
-	attr.type = CKA_BITS_PER_PIXEL;
-	attr.pValue = &twentyfour;
-	attr.ulValueLen = sizeof (twentyfour);
+	g_object_unref (data_file);
+	data_file = NULL;
 	
-	gck_store_register_schema (store, &attr, NULL, GCK_STORE_IS_INTERNAL);
+	if (public_fd != -1)
+		close (public_fd);
+	if (private_fd != -1)
+		close (private_fd);
+	if (write_fd != -1)
+		close (write_fd);
+	public_fd = private_fd = write_fd = -1;
 	
-	transaction = gck_transaction_new ();
-	object = g_object_new (GCK_TYPE_OBJECT, NULL);
-	prv_object = g_object_new (GCK_TYPE_OBJECT, NULL);
+	g_object_unref (login);
+}
+
+DEFINE_TEST(test_file_create)
+{
+	GckDataResult res;
 	
-	gck_file_store_connect_entry (file_store, "unique-one", object);
-	gck_file_store_connect_entry (file_store, "unique-private", prv_object);
+	res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+
+	/* Should be able to create private in a new file */
+	res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PRIVATE);
+	g_assert (res == GCK_DATA_SUCCESS);
 }
 
-DEFINE_TEARDOWN(file_store)
+DEFINE_TEST(test_file_write_value)
 {
-	g_free (test_filename);
+	GckDataResult res;
 	
-	gck_file_store_disconnect_entry (file_store, "unique-private", prv_object);
+	/* Can't write when no identifier present */
+	res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+	g_assert (res == GCK_DATA_UNRECOGNIZED);
 	
-	if (prv_object != NULL)
-		g_object_unref (prv_object);
-	prv_object = NULL;
+	res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+
+	/* Should be able to write now */
+	res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+	g_assert (res == GCK_DATA_SUCCESS);
+}
 
-	g_object_unref (file_store);
-	file_store = NULL;
-	store = NULL;
+DEFINE_TEST(test_file_read_value)
+{
+	gconstpointer value = NULL;
+	GckDataResult res;
+	gsize n_value;
+	guint number = 7778;
+	
+	/* Write some stuff in */
+	res = gck_data_file_create_entry (data_file, "ident", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+	res = gck_data_file_write_value (data_file, "ident", CKA_LABEL, "TWO-label", 10);
+	g_assert (res == GCK_DATA_SUCCESS);
+	res = gck_data_file_write_value (data_file, "ident", CKA_VALUE, &number, sizeof (number));
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	/* Read for an invalid item */
+	res = gck_data_file_read_value (data_file, "non-existant", CKA_LABEL, &value, &n_value);
+	g_assert (res == GCK_DATA_UNRECOGNIZED);
+	
+	/* Read for an invalid attribute */
+	res = gck_data_file_read_value (data_file, "ident", CKA_ID, &value, &n_value);
+	g_assert (res == GCK_DATA_UNRECOGNIZED);
+	
+	/* Read out a valid number */
+	res = gck_data_file_read_value (data_file, "ident", CKA_VALUE, &value, &n_value);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (value);
+	g_assert (n_value == sizeof (number));
+	g_assert_cmpuint (*((guint*)value), ==, number);
 	
-	g_object_unref (transaction);
-	transaction = NULL;
+	/* Read out the valid string */
+	res = gck_data_file_read_value (data_file, "ident", CKA_LABEL, &value, &n_value);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (value);
+	g_assert (n_value == 10);
+	g_assert_cmpstr ((const gchar*)value, ==, "TWO-label");
+}
+
+DEFINE_TEST(test_file_read)
+{
+	GckDataResult res;
 	
-	if (object != NULL)
-		g_object_unref (object);
-	object = NULL;
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
 }
 
-DEFINE_TEST(test_properties)
+DEFINE_TEST(test_file_lookup)
 {
-	const gchar *filename;
-	gboolean locked;
-	gchar *name;
-		
-	filename = gck_file_store_get_filename (file_store);
-	g_assert_cmpstr (filename, ==, test_filename);
+	GckDataResult res;
+	guint section;
+	gboolean ret;
+	
+	/* Invalid shouldn't succeed */
+	ret = gck_data_file_lookup_entry (data_file, "non-existant", &section);
+	g_assert (ret == FALSE);
+
+	/* Create a test item */
+	res = gck_data_file_create_entry (data_file, "test-ident", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	locked = gck_file_store_get_locked (file_store);
-	g_assert (locked == TRUE);
+	ret = gck_data_file_lookup_entry (data_file, "test-ident", &section);
+	g_assert (ret == TRUE);
+	g_assert (section == GCK_DATA_FILE_SECTION_PUBLIC);
 	
-	/* Try properties */
-	locked = FALSE;
-	g_object_get (file_store, "filename", &name, "locked", &locked, NULL);
-	g_assert_cmpstr (name, ==, test_filename);
-	g_assert (locked == TRUE);
+	/* Should be able to call without asking for section */
+	ret = gck_data_file_lookup_entry (data_file, "test-ident", NULL);
+	g_assert (ret == TRUE);
 }
 
-DEFINE_TEST(test_store_read)
+DEFINE_TEST(file_read_private_without_login)
 {
+	GckDataResult res;
+	guint section;
+	gconstpointer value;
+	gsize n_value;
 	gboolean ret;
 	
-	ret = gck_file_store_refresh (file_store);
+	res = gck_data_file_read_fd (data_file, private_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	/* Items from the private section should exist */
+	ret = gck_data_file_lookup_entry (data_file, "identifier-private", &section);
 	g_assert (ret);
+	g_assert (section == GCK_DATA_FILE_SECTION_PRIVATE);
+	
+	/* But we shouldn't be able to read values from those private items */
+	ret = gck_data_file_read_value (data_file, "identifier-private", CKA_LABEL, &value, &n_value);
+	g_assert (ret == GCK_DATA_LOCKED);
+	
+	/* Shouldn't be able to create private items */
+	res = gck_data_file_create_entry (data_file, "dummy-private", GCK_DATA_FILE_SECTION_PRIVATE);
+	g_assert (res == GCK_DATA_LOCKED);
+	
+	/* Shouldn't be able to write with another login */
+	res = gck_data_file_write_fd (data_file, write_fd, login);
+	g_assert (res == GCK_DATA_LOCKED);
+	
+	/* Now load a public file without private bits*/
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	/* Now we should be able to load private stuff */
+	res = gck_data_file_create_entry (data_file, "dummy-private", GCK_DATA_FILE_SECTION_PRIVATE);
+	g_assert (res == GCK_DATA_SUCCESS);
 }
 
-DEFINE_TEST(test_unlock)
+DEFINE_TEST(test_file_write)
 {
-	gchar *str;
-	CK_RV rv;
+	GckDataResult res;
+
+	res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	/* We shouldn't be able to read from private object */
-	g_assert (!gck_file_store_have_entry (file_store, "unique-private"));
+	res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	/* Try with wrong password */
-	rv = gck_file_store_unlock (file_store, (guchar*)"password", 8);
-	g_assert (rv == CKR_PIN_INCORRECT);
-	g_assert (gck_file_store_get_locked (file_store) == TRUE);
+	res = gck_data_file_create_entry (data_file, "identifier-two", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	/* A valid unlock */
-	rv = gck_file_store_unlock (file_store, (guchar*)"booo", 4);
-	g_assert (rv == CKR_OK);
-	g_assert (gck_file_store_get_locked (file_store) == FALSE);
+	res = gck_data_file_write_fd (data_file, write_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+}
 
-	/* Unlocking twice should result in this code */
-	rv = gck_file_store_unlock (file_store, (guchar*)"booo", 4);
-	g_assert (rv == CKR_USER_ALREADY_LOGGED_IN);
-	g_assert (gck_file_store_get_locked (file_store) == FALSE);
+DEFINE_TEST(cant_write_private_without_login)
+{
+	GckDataResult res;
+	
+	res = gck_data_file_create_entry (data_file, "identifier_private", GCK_DATA_FILE_SECTION_PRIVATE);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	res = gck_data_file_write_fd (data_file, write_fd, NULL);
+	g_assert (res == GCK_DATA_LOCKED);
+}
 
-	/* Now we should be able to read from private object */
-	g_assert (gck_file_store_have_entry (file_store, "unique-private"));
-	str = gck_store_read_string (store, prv_object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "private-label");
-	g_free (str);
+DEFINE_TEST(write_private_with_login)
+{
+	GckDataResult res;
+	gulong value;
 	
-	/* Now lock again */
-	rv = gck_file_store_lock (file_store);
-	g_assert (rv == CKR_OK);
+	res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+	res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	/* Locking twice should result in this code */
-	rv = gck_file_store_lock (file_store);
-	g_assert (rv == CKR_USER_NOT_LOGGED_IN);
+	res = gck_data_file_create_entry (data_file, "identifier-two", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+	res = gck_data_file_write_value (data_file, "identifier-two", CKA_LABEL, "TWO-label", 9);
+	g_assert (res == GCK_DATA_SUCCESS);
+	value = 555;
+	res = gck_data_file_write_value (data_file, "identifier-two", CKA_VALUE, &value, sizeof (value));
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	res = gck_data_file_create_entry (data_file, "identifier-private", GCK_DATA_FILE_SECTION_PRIVATE);
+	g_assert (res == GCK_DATA_SUCCESS);
+	res = gck_data_file_write_value (data_file, "identifier-private", CKA_LABEL, "private-label", 13);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	/* We should get default attributes */
-	str = gck_store_read_string (store, prv_object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "label");
-	g_free (str);
+	res = gck_data_file_write_fd (data_file, write_fd, login);
+	g_assert (res == GCK_DATA_SUCCESS);
 }
 
-DEFINE_TEST(write_encrypted)
+DEFINE_TEST(read_private_with_login)
 {
-	CK_ATTRIBUTE attr;
-	gboolean ret;
-	gchar *str;
-	CK_RV rv;
+	GckDataResult res;
+	gconstpointer value;
+	gsize n_value;
+	
+	res = gck_data_file_read_fd (data_file, private_fd, login);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	rv = gck_file_store_unlock (file_store, (guchar*)"booo", 4);
-	g_assert (rv == CKR_OK);
+	/* Should be able to read private items */
+	res = gck_data_file_read_value (data_file, "identifier-private", CKA_LABEL, &value, &n_value);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert_cmpuint (n_value, ==, 13);
+	g_assert (memcmp (value, "private-label", 13) == 0);
+}
 
-	attr.type = CKA_LABEL;
-	attr.pValue = "private-label-two";
-	attr.ulValueLen = 17;
+DEFINE_TEST(destroy_entry)
+{
+	GckDataResult res;
 	
-	gck_store_set_attribute (store, transaction, prv_object, &attr);
+	res = gck_data_file_destroy_entry (data_file, "non-existant");
+	g_assert (res == GCK_DATA_UNRECOGNIZED);
 	
-	gck_transaction_complete (transaction);
-	g_assert (gck_transaction_get_result (transaction) == CKR_OK);
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	ret = gck_file_store_refresh (file_store);
-	g_assert (ret);
+	/* Make sure it's here */
+	g_assert (gck_data_file_lookup_entry (data_file, "identifier-public", NULL));
 	
-	str = gck_store_read_string (store, prv_object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "private-label-two");
-	g_free (str);
+	res = gck_data_file_destroy_entry (data_file, "identifier-public");
+	g_assert (res == GCK_DATA_SUCCESS);
+
+	/* Make sure it's gone */
+	g_assert (!gck_data_file_lookup_entry (data_file, "identifier-public", NULL));
 }
 
-DEFINE_TEST(file_set_get_attribute)
+DEFINE_TEST(destroy_entry_by_loading)
 {
-	gchar buffer[16];
-	CK_ATTRIBUTE attr;
-	CK_RV rv;
-	
-	attr.type = CKA_LABEL;
-	attr.pValue = "booyah";
-	attr.ulValueLen = 6;
+	GckDataResult res;
 	
-	gck_store_set_attribute (store, transaction, object, &attr);
+	/* Create some extra idenifiers */
+	res = gck_data_file_create_entry (data_file, "my-public", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+	res = gck_data_file_create_entry (data_file, "my-private", GCK_DATA_FILE_SECTION_PRIVATE);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	gck_transaction_complete (transaction);
-	g_assert (gck_transaction_get_result (transaction) == CKR_OK);
+	/* Now read from the file */
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	attr.pValue = buffer;
-	attr.ulValueLen = 7;
-	rv = gck_store_get_attribute (store, object, &attr);
-	g_assert (rv == CKR_OK);
-	g_assert (attr.ulValueLen == 6);
-	g_assert (memcmp (attr.pValue, "booyah", 6) == 0);
+	/* Both should be gone */
+	g_assert (!gck_data_file_lookup_entry (data_file, "my-public", NULL));
+	g_assert (!gck_data_file_lookup_entry (data_file, "my-private", NULL));
 }
 
-DEFINE_TEST(file_write_read_value)
+
+DEFINE_TEST(destroy_private_without_login)
 {
-	CK_ATTRIBUTE attr;
-	CK_ULONG five = 5;
-	gconstpointer value;
-	gsize n_value;
+	GckDataResult res;
 	
-	attr.type = CKA_BITS_PER_PIXEL;
-	attr.pValue = &five;
-	attr.ulValueLen = sizeof (five);
+	res = gck_data_file_read_fd (data_file, private_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
 	
-	gck_store_write_value (store, transaction, object, &attr);
-
-	gck_transaction_complete (transaction);
-	g_assert (gck_transaction_get_result (transaction) == CKR_OK);
-
-	value = gck_store_read_value (store, object, CKA_BITS_PER_PIXEL, &n_value);
-	g_assert (value);
-	g_assert (n_value == sizeof (five));
-	g_assert (memcmp (value, &five, sizeof (five)) == 0);
+	/* Make sure it's here */
+	g_assert (gck_data_file_lookup_entry (data_file, "identifier-private", NULL));
+	
+	/* Shouldn't be able to destroy */
+	res = gck_data_file_destroy_entry (data_file, "identifier-private");
+	g_assert (res == GCK_DATA_LOCKED);
+	
+	/* Make sure it's still here */
+	g_assert (gck_data_file_lookup_entry (data_file, "identifier-private", NULL));
 }
 
-DEFINE_TEST(destroy_entry)
+static void
+entry_added_one (GckDataFile *df, const gchar *identifier, gboolean *added)
 {
-	gboolean ret;
-	gchar *str;
+	g_assert (GCK_IS_DATA_FILE (df));
+	g_assert (df == data_file);
+	g_assert (identifier);
+	g_assert (added);
 	
-	str = gck_store_read_string (store, object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "public-label");
-	g_free (str);
+	/* Should only be called once */
+	g_assert (!*added);
+	*added = TRUE;
+}
 
-	gck_file_store_destroy_entry (file_store, transaction, "unique-one");
-	gck_transaction_complete (transaction);
+DEFINE_TEST(entry_added_signal)
+{
+	GckDataResult res;
+	gboolean added;
 	
-	g_assert (gck_transaction_get_result (transaction) == CKR_OK);
-	g_assert (!gck_transaction_get_failed (transaction));
+	g_signal_connect (data_file, "entry-added", G_CALLBACK (entry_added_one), &added);
 	
-	ret = gck_file_store_refresh (file_store);
-	g_assert (ret);
+	/* Should fire the signal */
+	added = FALSE;
+	res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (added == TRUE);
 	
-	str = gck_store_read_string (store, object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "label");
-	g_free (str);
+	/* Another one should be added when we load */
+	added = FALSE;
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (added == TRUE);
 }
 
-DEFINE_TEST(refresh_modifications)
+static void
+entry_changed_one (GckDataFile *df, const gchar *identifier, gulong type, gboolean *changed)
 {
-	GckFileStore *fs;
-	gboolean ret;
-	gchar *str;
+	g_assert (GCK_IS_DATA_FILE (df));
+	g_assert (df == data_file);
+	g_assert (identifier);
+	g_assert (changed);
+	g_assert (type == CKA_LABEL);
+	
+	/* Should only be called once */
+	g_assert (!*changed);
+	*changed = TRUE;
+}
+
+DEFINE_TEST(entry_changed_signal)
+{
+	GckDataResult res;
+	gboolean changed;
+	
+	g_signal_connect (data_file, "entry-changed", G_CALLBACK (entry_changed_one), &changed);
+	
+	/* Loading shouldn't fire the signal */
+	changed = FALSE;
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (changed == FALSE);
+	
+	/* Shouldn't fire the signal on nonexistant */
+	changed = FALSE;
+	res = gck_data_file_write_value (data_file, "non-existant", CKA_LABEL, "new-value", 10);
+	g_assert (res == GCK_DATA_UNRECOGNIZED);
+	g_assert (changed == FALSE);
+	
+	/* Should fire the signal */
+	changed = FALSE;
+	res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "new-value", 10);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (changed == TRUE);
+
+	/* Shouldn't fire the signal, same value again */
+	changed = FALSE;
+	res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "new-value", 10);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (changed == FALSE);
+	
+	/* Reload file, should revert, fire signal */
+	changed = FALSE;
+	g_assert (lseek (public_fd, 0, SEEK_SET) != -1);
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (changed == TRUE);
+}
 
-	/* Check that our label is correct */
-	str = gck_store_read_string (store, object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "public-label");
-	g_free (str);
-
-	/* Open a second file store on the same file */
-	fs = gck_file_store_new (test_filename);
-	ret = gck_file_store_refresh (fs);
-	g_assert (ret);
-	
-	/* Delete something from other store */
-	gck_file_store_destroy_entry (fs, transaction, "unique-one");
-	gck_transaction_complete (transaction);
-	g_assert (gck_transaction_get_result (transaction) == CKR_OK);
-	g_assert (!gck_transaction_get_failed (transaction));
+static void
+entry_removed_one (GckDataFile *df, const gchar *identifier, gboolean *removed)
+{
+	g_assert (GCK_IS_DATA_FILE (df));
+	g_assert (df == data_file);
+	g_assert (identifier);
+	g_assert (removed);
+	
+	/* Should only be called once */
+	g_assert (!*removed);
+	*removed = TRUE;
+}
+
+DEFINE_TEST(entry_removed_signal)
+{
+	GckDataResult res;
+	gboolean removed;
+	
+	g_signal_connect (data_file, "entry-removed", G_CALLBACK (entry_removed_one), &removed);
+	
+	/* Loading shouldn't fire the signal */
+	removed = FALSE;
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (removed == FALSE);
+	
+	/* Shouldn't fire the signal on removing nonexistant */
+	removed = FALSE;
+	res = gck_data_file_destroy_entry (data_file, "non-existant");
+	g_assert (res == GCK_DATA_UNRECOGNIZED);
+	g_assert (removed == FALSE);
+	
+	/* Remove a real entry */
+	removed = FALSE;
+	res = gck_data_file_destroy_entry (data_file, "identifier-public");
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (removed == TRUE);
+	
+	/* Add a dummy entry */
+	res = gck_data_file_create_entry (data_file, "extra-dummy", GCK_DATA_FILE_SECTION_PUBLIC);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	/* That one should go away when we reload, fire signal */
+	removed = FALSE;
+	g_assert (lseek (public_fd, 0, SEEK_SET) != -1);
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (removed == TRUE);
+}
 
-	/* Refresh first file store */
-	ret = gck_file_store_refresh (file_store);
-	g_assert (ret);
+static void
+foreach_entry (GckDataFile *df, const gchar *identifier, gpointer data)
+{
+	GPtrArray *array = data;
+	const gchar *ident;
+	int i;
+	
+	g_assert (data);
+	g_assert (identifier);
+	g_assert (GCK_IS_DATA_FILE (df));
 	
-	/* Should be gone, we should see default label */
-	str = gck_store_read_string (store, object, CKA_LABEL);
-	g_assert_cmpstr (str, ==, "label");
-	g_free (str);
+	/* Check that this is unique */
+	for (i = 0; i < array->len; ++i) {
+		ident = g_ptr_array_index (array, i);
+		g_assert (ident);
+		g_assert_cmpstr (ident, !=, identifier);
+	}
 	
-	g_object_unref (fs);
+	/* Add it */
+	g_ptr_array_add (array, g_strdup (identifier));
 }
 
-DEFINE_TEST(file_store_revert_first)
+DEFINE_TEST(data_file_foreach)
 {
-	CK_ATTRIBUTE attr, prev;
-	gconstpointer value;
-	gsize n_value;
+	GckDataResult res;
+	GPtrArray *array;
 	
-	prev.type = CKA_LABEL;
-	prev.pValue = "numberone";
-	prev.ulValueLen = 9;
-
-	/* Change the attribute */
-	gck_store_set_attribute (store, transaction, object, &prev);
-	gck_transaction_complete (transaction);
-	g_assert (gck_transaction_get_failed (transaction) == FALSE);
-
-	/* Value should be new value */
-	value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
-	g_assert (value && n_value == prev.ulValueLen);
-	g_assert (memcmp (prev.pValue, value, n_value) == 0);
-
-	/* A new transaction */
-	g_object_unref (transaction);
-	transaction = gck_transaction_new ();
+	res = gck_data_file_read_fd (data_file, private_fd, login);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	array = g_ptr_array_new ();
+	gck_data_file_foreach_entry (data_file, foreach_entry, array);
+	g_assert (array->len == 4);
+	
+	g_ptr_array_add (array, NULL);
+	g_strfreev ((gchar**)g_ptr_array_free (array, FALSE));
+}
 
-	attr.type = CKA_LABEL;
-	attr.pValue = "second";
-	attr.ulValueLen = 6;
+DEFINE_TEST(unique_entry)
+{
+	GckDataResult res;
+	gchar *identifier;
+	
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	/* Should change an identifier that conflicts */
+	identifier = g_strdup ("identifier-public"); 
+	res = gck_data_file_unique_entry (data_file, &identifier);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert_cmpstr (identifier, !=, "identifier-public");
+	g_free (identifier);
 
-	gck_store_set_attribute (store, transaction, object, &attr);
-	g_assert (gck_transaction_get_failed (transaction) == FALSE);
+	/* Shouldn't change a unique identifier */
+	identifier = g_strdup ("identifier-unique"); 
+	res = gck_data_file_unique_entry (data_file, &identifier);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert_cmpstr (identifier, ==, "identifier-unique");
+	g_free (identifier);
+	
+	/* Should be able to get from NULL */
+	identifier = NULL;
+	res = gck_data_file_unique_entry (data_file, &identifier);
+	g_assert (res == GCK_DATA_SUCCESS);
+	g_assert (identifier != NULL);
+	g_assert (identifier[0] != 0);
+	g_free (identifier);
+}
 
-	/* Should get new value */
-	value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
-	g_assert (value && n_value == attr.ulValueLen);
-	g_assert (memcmp (attr.pValue, value, n_value) == 0);
+DEFINE_TEST(have_sections)
+{
+	GckDataResult res;
+	
+	res = gck_data_file_read_fd (data_file, public_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
+	
+	/* No private section */
+	g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+	g_assert (!gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
 
-	attr.type = CKA_LABEL;
-	attr.pValue = "third";
-	attr.ulValueLen = 5;
+	/* Read private stuff into file, without login */
+	res = gck_data_file_read_fd (data_file, private_fd, NULL);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	gck_store_set_attribute (store, transaction, object, &attr);
-	g_assert (gck_transaction_get_failed (transaction) == FALSE);
+	/* Should have a private section even without login */
+	g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+	g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
 
-	/* Should get new value */
-	value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
-	g_assert (value && n_value == attr.ulValueLen);
-	g_assert (memcmp (attr.pValue, value, n_value) == 0);
-	
-	/* Fail for some arbitrary reason */
-	gck_transaction_fail (transaction, CKR_ATTRIBUTE_VALUE_INVALID);
-	
-	/* Value should not have changed yet */
-	value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
-	g_assert (value && n_value == attr.ulValueLen);
-	g_assert (memcmp (attr.pValue, value, n_value) == 0);
-	
-	/* Now complete the transaction */
-	gck_transaction_complete (transaction);
-	g_assert (gck_transaction_get_failed (transaction) == TRUE);
+	/* Read private stuff into file, with login */
+	g_assert (lseek (private_fd, 0, SEEK_SET) == 0);
+	res = gck_data_file_read_fd (data_file, private_fd, login);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	/* Value should now have changed, back to default */
-	value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
-	g_assert (value && n_value == prev.ulValueLen);
-	g_assert (memcmp (prev.pValue, value, n_value) == 0);
-}
+	/* Should have a private section now with login */
+	g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+	g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
 
-DEFINE_TEST(file_store_nonexistant)
-{
-	GckFileStore *fs;
-	gboolean ret;
+	/* Read public stuff back into file*/
+	g_assert (lseek (public_fd, 0, SEEK_SET) == 0);
+	res = gck_data_file_read_fd (data_file, public_fd, login);
+	g_assert (res == GCK_DATA_SUCCESS);
 
-	/* Should be able to read from a nonexistant file store */
-	fs = gck_file_store_new ("./nonexistant");
-	ret = gck_file_store_refresh (fs);
-	g_assert (ret);
-		
-	g_object_unref (fs);
+	/* Shouldn't have a private section now  */
+	g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+	g_assert (!gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
 }

Modified: trunk/pkcs11/gck/tests/unit-test-data-openssl.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-data-openssl.c	(original)
+++ trunk/pkcs11/gck/tests/unit-test-data-openssl.c	Sat Jan 17 20:17:52 2009
@@ -35,19 +35,6 @@
 #include <stdio.h>
 #include <string.h>
 
-static void
-read_file (const gchar *filename, guchar **contents, gsize *len)
-{
-	gchar *path;
-	gboolean ret;
-	
-	path = g_build_filename (g_get_current_dir (), "test-data", filename, NULL);
-	ret = g_file_get_contents (path, (gchar**)contents, len, NULL);
-	g_assert ("couldn't read in file" && ret);
-	
-	g_free (path);
-}
-
 guchar *refenc = NULL;
 guchar *refdata = NULL;
 gsize n_refenc = 0;
@@ -90,7 +77,7 @@
 	gsize n_input;
 	guint num;
 	
-	read_file ("pem-rsa-enc.key", &input, &n_input);
+	input = test_read_testdata ("pem-rsa-enc.key", &n_input);
 
 	num = gck_data_pem_parse (input, n_input, parse_reference, NULL);
 	g_assert ("couldn't PEM block in reference data" && num == 1);

Added: trunk/pkcs11/gck/tests/unit-test-login.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/tests/unit-test-login.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,120 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-login.c: Test gck-login.c 
+
+   Copyright (C) 2007 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 <stef memberwebs com>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "run-auto-test.h"
+
+#include "gck/gck-login.h"
+
+DEFINE_TEST(test_login)
+{
+	GckLogin *login;
+	const gchar *password;
+	gsize n_password;
+	
+	login = gck_login_new ((CK_UTF8CHAR_PTR)"test-pin", 8);
+	g_assert (GCK_IS_LOGIN (login));
+	
+	password = gck_login_get_password (login, &n_password);
+	g_assert (password);
+	g_assert_cmpuint (n_password, ==, 8);
+	g_assert (memcmp (password, "test-pin", 8) == 0);
+	
+	g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pin", 8));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+	g_assert (!gck_login_equals (login, NULL, 0));
+	g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pin", -1));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+	
+	g_object_unref (login);
+}
+
+DEFINE_TEST(test_null_terminated)
+{
+	GckLogin *login;
+	const gchar *password;
+	gsize n_password;
+	
+	login = gck_login_new ((CK_UTF8CHAR_PTR)"null-terminated", -1);
+	g_assert (GCK_IS_LOGIN (login));
+	
+	password = gck_login_get_password (login, &n_password);
+	g_assert (password);
+	g_assert_cmpstr (password, ==, "null-terminated");
+	g_assert_cmpuint (n_password, ==, strlen ("null-terminated"));
+	
+	g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", strlen ("null-terminated")));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+	g_assert (!gck_login_equals (login, NULL, 0));
+	g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", -1));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+	g_object_unref (login);
+}
+
+DEFINE_TEST(test_null)
+{
+	GckLogin *login;
+	const gchar *password;
+	gsize n_password;
+	
+	login = gck_login_new (NULL, 0);
+	g_assert (GCK_IS_LOGIN (login));
+	
+	password = gck_login_get_password (login, &n_password);
+	g_assert (password == NULL);
+	g_assert_cmpuint (n_password, ==, 0);
+	
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", strlen ("null-terminated")));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+	g_assert (gck_login_equals (login, NULL, 0));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", -1));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+	g_object_unref (login);
+}
+
+DEFINE_TEST(test_empty)
+{
+	GckLogin *login;
+	const gchar *password;
+	gsize n_password;
+	
+	login = gck_login_new ((CK_UTF8CHAR_PTR)"", 0);
+	g_assert (GCK_IS_LOGIN (login));
+	
+	password = gck_login_get_password (login, &n_password);
+	g_assert_cmpstr (password, ==, "");
+	g_assert_cmpuint (n_password, ==, 0);
+	
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", strlen ("null-terminated")));
+	g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+	g_assert (!gck_login_equals (login, NULL, 0));
+	g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"", -1));
+	g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+	g_object_unref (login);
+}

Modified: trunk/pkcs11/gck/tests/unit-test-transaction.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-transaction.c	(original)
+++ trunk/pkcs11/gck/tests/unit-test-transaction.c	Sat Jan 17 20:17:52 2009
@@ -186,3 +186,149 @@
 
 	g_object_unref (transaction);
 }
+
+DEFINE_TEST(remove_file_success)
+{
+	GckTransaction *transaction = gck_transaction_new ();
+	gchar *filename = test_build_filename ("remove-file");
+	
+	g_assert (g_file_set_contents (filename, "xxx", 3, NULL));
+	g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+	
+	gck_transaction_remove_file (transaction, filename);
+	g_assert (!gck_transaction_get_failed (transaction));
+	
+	g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+	
+	gck_transaction_complete (transaction);
+	g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+	
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST(remove_file_abort)
+{
+	GckTransaction *transaction = gck_transaction_new ();
+	gchar *filename = test_build_filename ("remove-file");
+	gchar *data;
+	gsize n_data;
+	
+	g_assert (g_file_set_contents (filename, "xxx", 3, NULL));
+	g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+	
+	gck_transaction_remove_file (transaction, filename);
+	g_assert (!gck_transaction_get_failed (transaction));
+	
+	g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+	
+	/* Fail the transaction */
+	gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+	
+	gck_transaction_complete (transaction);
+	g_assert (gck_transaction_get_failed (transaction));
+	g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+	
+	g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+	g_assert_cmpuint (n_data, ==, 3);
+	g_assert_cmpstr (data, ==, "xxx");
+	g_free (data);
+	
+	g_unlink (filename);
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST(remove_file_non_exist)
+{
+	GckTransaction *transaction = gck_transaction_new ();
+	gchar *filename = test_build_filename ("remove-non-existant");
+
+	g_unlink (filename);
+	
+	/* Should succeed even though not exist */
+	gck_transaction_remove_file (transaction, filename);
+	g_assert (!gck_transaction_get_failed (transaction));
+	
+	gck_transaction_complete (transaction);
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST(write_file)
+{
+	GckTransaction *transaction = gck_transaction_new ();
+	gchar *filename = test_build_filename ("write-test");
+	gchar *data;
+	gsize n_data;
+	
+	gck_transaction_write_file (transaction, filename, (const guchar*)"value", 5);
+	g_assert (!gck_transaction_get_failed (transaction));
+
+	g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+	g_assert_cmpuint (n_data, ==, 5);
+	g_assert_cmpstr (data, ==, "value");
+	g_free (data);
+
+	gck_transaction_complete (transaction);
+	
+	g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+	g_assert_cmpuint (n_data, ==, 5);
+	g_assert_cmpstr (data, ==, "value");
+	g_free (data);
+
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST(write_file_abort_gone)
+{
+	GckTransaction *transaction = gck_transaction_new ();
+	gchar *filename = test_build_filename ("write-test");
+	gchar *data;
+	gsize n_data;
+	
+	g_unlink (filename);
+	
+	gck_transaction_write_file (transaction, filename, (const guchar*)"value", 5);
+	g_assert (!gck_transaction_get_failed (transaction));
+
+	g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+	g_assert_cmpuint (n_data, ==, 5);
+	g_assert_cmpstr (data, ==, "value");
+	g_free (data);
+
+	gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+	gck_transaction_complete (transaction);
+
+	g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST(write_file_abort_revert)
+{
+	GckTransaction *transaction = gck_transaction_new ();
+	gchar *filename = test_build_filename ("write-test");
+	gchar *data;
+	
+	g_assert (g_file_set_contents (filename, "my original", -1, NULL));
+	
+	gck_transaction_write_file (transaction, filename, (const guchar*)"new value", 9);
+	g_assert (!gck_transaction_get_failed (transaction));
+
+	g_assert (g_file_get_contents (filename, &data, NULL, NULL));
+	g_assert_cmpstr (data, ==, "new value");
+	g_free (data);
+
+	gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+	gck_transaction_complete (transaction);
+
+	g_assert (g_file_get_contents (filename, &data, NULL, NULL));
+	g_assert_cmpstr (data, ==, "my original");
+	g_free (data);
+
+	g_object_unref (transaction);
+	g_free (filename);
+}

Added: trunk/pkcs11/gck/tests/unit-test-util.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/tests/unit-test-util.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,69 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-util.c: Test gck-util.c 
+
+   Copyright (C) 2007 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 <stef memberwebs com>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "run-auto-test.h"
+
+#include "gck/gck-util.h"
+
+static const guchar TEST_DATA[] = { 0x05, 0xD6, 0x95, 0x96, 0x10, 0x12, 0xAE, 0x35 };
+static const gchar *TEST_HEX = "05D695961012AE35";
+static const gchar *TEST_HEX_SPACE = "\n05 D695 \r961012AE35\n\n";
+
+DEFINE_TEST(hex_encode)
+{
+	gchar *hex;
+	
+	hex = gck_util_hex_encode (TEST_DATA, sizeof (TEST_DATA));
+	g_assert (hex);
+	g_assert_cmpstr (hex, ==, TEST_HEX);
+}
+
+DEFINE_TEST(hex_decode)
+{
+	guchar *data;
+	gsize n_data;
+	
+	data = gck_util_hex_decode (TEST_HEX, -1, &n_data);
+	g_assert (data);
+	g_assert (n_data == sizeof (TEST_DATA));
+	g_assert (memcmp (data, TEST_DATA, n_data) == 0);
+
+	/* Spaces should be ignored */
+	data = gck_util_hex_decode (TEST_HEX_SPACE, -1, &n_data);
+	g_assert (data);
+	g_assert (n_data == sizeof (TEST_DATA));
+	g_assert (memcmp (data, TEST_DATA, n_data) == 0);
+
+	/* Invalid input, null out */
+	data = gck_util_hex_decode ("AB", 1, &n_data);
+	g_assert (!data);
+
+	/* Nothing in, empty out */
+	data = gck_util_hex_decode ("AB", 0, &n_data);
+	g_assert (data);
+	g_assert (n_data == 0);
+}

Modified: trunk/pkcs11/roots-store/gck-roots-module.c
==============================================================================
--- trunk/pkcs11/roots-store/gck-roots-module.c	(original)
+++ trunk/pkcs11/roots-store/gck-roots-module.c	Sat Jan 17 20:17:52 2009
@@ -27,6 +27,7 @@
 
 #include "gck/gck-data-pem.h"
 #include "gck/gck-file-tracker.h"
+#include "gck/gck-serializable.h"
 
 #include <string.h>
 
@@ -117,7 +118,7 @@
 	/* Create a new certificate object */
 	cert = GCK_CERTIFICATE (gck_roots_certificate_new (unique, path));
 
-	if (!gck_certificate_load_data (cert, data, n_data)) {
+	if (!gck_serializable_load (GCK_SERIALIZABLE (cert), NULL, data, n_data)) {
 		g_message ("couldn't parse certificate(s): %s", path);
 		g_object_unref (cert);
 		return NULL;

Modified: trunk/pkcs11/ssh-store/gck-ssh-module.c
==============================================================================
--- trunk/pkcs11/ssh-store/gck-ssh-module.c	(original)
+++ trunk/pkcs11/ssh-store/gck-ssh-module.c	Sat Jan 17 20:17:52 2009
@@ -48,8 +48,8 @@
 	"SSH Keys",
 	"Gnome Keyring",
 	"1.0",
-	"1",
-	CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED,
+	"1", /* Unique serial number for manufacturer */
+	CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED | CKF_USER_PIN_INITIALIZED,
 	CK_EFFECTIVELY_INFINITE,
 	CK_EFFECTIVELY_INFINITE,
 	CK_EFFECTIVELY_INFINITE,

Modified: trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c
==============================================================================
--- trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c	(original)
+++ trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c	Sat Jan 17 20:17:52 2009
@@ -49,19 +49,6 @@
 
 #define COMMENT "A public key comment"
 
-static void
-read_file (const gchar *filename, guchar **contents, gsize *len)
-{
-	gchar *path;
-	gboolean ret;
-	
-	path = g_build_filename (g_get_current_dir (), "test-data", filename, NULL);
-	ret = g_file_get_contents (path, (gchar**)contents, len, NULL);
-	g_assert ("couldn't read in file" && ret);
-	
-	g_free (path);
-}
-
 DEFINE_TEST(parse_public)
 {
 	gcry_sexp_t sexp;
@@ -76,7 +63,7 @@
 	
 	for (i = 0; i < G_N_ELEMENTS (PUBLIC_FILES); ++i) {
 		
-		read_file (PUBLIC_FILES[i], &data, &n_data);
+		data = test_read_testdata (PUBLIC_FILES[i], &n_data);
 		
 		res = gck_ssh_openssh_parse_public_key (data, n_data, &sexp, &comment);
 		if (res != GCK_DATA_SUCCESS) {
@@ -110,7 +97,7 @@
 	
 	for (i = 0; i < G_N_ELEMENTS (PRIVATE_FILES); ++i) {
 		
-		read_file (PRIVATE_FILES[i], &data, &n_data);
+		data = test_read_testdata (PRIVATE_FILES[i], &n_data);
 		
 		res = gck_ssh_openssh_parse_private_key (data, n_data, "password", 8, &sexp);
 		if (res != GCK_DATA_SUCCESS) {

Added: trunk/pkcs11/user-store/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/Makefile.am	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,59 @@
+
+INCLUDES = \
+	-I$(top_builddir) \
+    	-I$(top_srcdir) \
+    	-I$(top_srcdir)/pkcs11 \
+    	$(GOBJECT_CFLAGS) \
+	$(LIBGCRYPT_CFLAGS) \
+	$(GLIB_CFLAGS)
+	
+			
+# ------------------------------------------------------------------------------
+# The user-store component code
+
+noinst_LTLIBRARIES = \
+	libgck-user-store.la
+
+libgck_user_store_la_SOURCES = \
+	gck-user-store.h \
+	gck-user-module.c gck-user-module.h \
+	gck-user-private-key.c gck-user-private-key.h \
+	gck-user-public-key.c gck-user-public-key.h \
+	gck-user-storage.c gck-user-storage.h
+
+# ------------------------------------------------------------------------------
+# The standalone module 
+
+moduledir = $(libdir)/gnome-keyring/devel/
+
+module_LTLIBRARIES = \
+	gck-user-store-standalone.la
+
+gck_user_store_standalone_la_LDFLAGS = \
+	-module -avoid-version \
+	-no-undefined -export-symbols-regex 'C_GetFunctionList'
+
+gck_user_store_standalone_la_SOURCES = \
+	gck-user-standalone.c
+
+gck_user_store_standalone_la_LIBADD = \
+	libgck-user-store.la \
+	$(top_builddir)/pkcs11/gck/libgck.la \
+	$(top_builddir)/common/libgkr-common-buffer.la \
+	$(top_builddir)/common/libgkr-common-secure.la \
+	$(GOBJECT_LIBS) \
+	$(GTHREAD_LIBS) \
+	$(GLIB_LIBS) \
+	$(LIBTASN1_LIBS) \
+	$(LIBGCRYPT_LIBS)
+
+
+# -------------------------------------------------------------------------------
+
+# if WITH_TESTS
+# TESTS_DIR = tests
+# else
+TESTS_DIR = 
+# endif
+
+SUBDIRS = . $(TESTS_DIR)

Added: trunk/pkcs11/user-store/gck-user-module.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-module.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,293 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-user-module.h"
+#include "gck-user-storage.h"
+#include "gck-user-store.h"
+
+#include "gck/gck-certificate.h"
+#include "gck/gck-data-asn1.h"
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-transaction.h"
+#include "gck/gck-util.h"
+
+#include <string.h>
+
+struct _GckUserModule {
+	GckModule parent;
+	GckUserStorage *storage;
+	gchar *directory;
+	GHashTable *logged_in_apps;
+};
+
+static const CK_SLOT_INFO gck_user_module_slot_info = {
+	"User Keys",
+	"Gnome Keyring",
+	CKF_TOKEN_PRESENT,
+	{ 0, 0 },
+	{ 0, 0 }
+};
+
+static const CK_TOKEN_INFO gck_user_module_token_info = {
+	"User Keys",
+	"Gnome Keyring",
+	"1.0",
+	"3", /* Unique serial number for manufacturer */
+	CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED | CKF_LOGIN_REQUIRED,
+	CK_EFFECTIVELY_INFINITE,
+	CK_EFFECTIVELY_INFINITE,
+	CK_EFFECTIVELY_INFINITE,
+	CK_EFFECTIVELY_INFINITE,
+	1024,
+	1,
+	CK_UNAVAILABLE_INFORMATION,
+	CK_UNAVAILABLE_INFORMATION,
+	CK_UNAVAILABLE_INFORMATION,
+	CK_UNAVAILABLE_INFORMATION,
+	{ 0, 0 },
+	{ 0, 0 },
+	""
+};
+
+#define UNUSED_VALUE (GUINT_TO_POINTER (1))
+
+G_DEFINE_TYPE (GckUserModule, gck_user_module, GCK_TYPE_MODULE);
+
+/* -----------------------------------------------------------------------------
+ * ACTUAL PKCS#11 Module Implementation 
+ */
+
+/* Include all the module entry points */
+#include "gck/gck-module-ep.h"
+GCK_DEFINE_MODULE (gck_user_module, GCK_TYPE_USER_MODULE);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL 
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static void 
+gck_user_module_real_parse_argument (GckModule *base, const gchar *name, const gchar *value)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	if (g_str_equal (name, "directory")) {
+		g_free (self->directory);
+		self->directory = g_strdup (value);
+	}
+}
+
+static CK_RV
+gck_user_module_real_refresh_token (GckModule *base)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	gck_user_storage_refresh (self->storage);
+	return CKR_OK;
+}
+
+static void 
+gck_user_module_real_store_token_object (GckModule *base, GckTransaction *transaction, GckObject *object)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	gck_user_storage_create (self->storage, transaction, object);
+}
+
+static void 
+gck_user_module_real_remove_token_object (GckModule *base, GckTransaction *transaction, GckObject *object)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	gck_user_storage_destroy (self->storage, transaction, object);
+}
+
+static CK_RV 
+gck_user_module_real_login_change (GckModule *base, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin, 
+                                   CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	GckLogin *old_login, *new_login;
+	GckTransaction *transaction;
+	CK_RV rv;
+	
+	/* 
+	 * Remember this doesn't affect the currently logged in user. Logged in 
+	 * sessions will remain logged in, and vice versa.
+	 */ 
+	
+	old_login = gck_login_new (old_pin, n_old_pin);
+	new_login = gck_login_new (new_pin, n_new_pin);
+	
+	transaction = gck_transaction_new ();
+	
+	gck_user_storage_relock (self->storage, transaction, old_login, new_login);
+	
+	g_object_unref (old_login);
+	g_object_unref (new_login);
+	
+	gck_transaction_complete (transaction);
+	rv = gck_transaction_get_result (transaction);
+	g_object_unref (transaction);
+	
+	return rv;
+}
+
+static CK_RV 
+gck_user_module_real_login_user (GckModule *base, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	GckLogin *login;
+	CK_RV rv;
+
+	/* See if this application has logged in */
+	if (g_hash_table_lookup (self->logged_in_apps, &slot_id))
+		return CKR_USER_ALREADY_LOGGED_IN;
+
+	login = gck_user_storage_get_login (self->storage);
+	
+	/* No application is logged in */
+	if (g_hash_table_size (self->logged_in_apps) == 0) {
+
+		g_return_val_if_fail (login == NULL, CKR_GENERAL_ERROR);
+
+		/* So actually unlock the store */
+		login = gck_login_new (pin, n_pin);
+		rv = gck_user_storage_unlock (self->storage, login);
+		g_object_unref (login);
+		
+	/* An application is already logged in */
+	} else {
+		
+		g_return_val_if_fail (login != NULL, CKR_GENERAL_ERROR);
+		
+		/* Compare our pin to the one used originally */
+		if (!gck_login_equals (login, pin, n_pin))
+			rv = CKR_PIN_INCORRECT;
+		else
+			rv = CKR_OK;
+	}
+
+	/* Note that this application logged in */
+	if (rv == CKR_OK)
+		g_hash_table_insert (self->logged_in_apps, gck_util_ulong_alloc (slot_id), UNUSED_VALUE);
+	
+	return rv;
+}
+
+static CK_RV 
+gck_user_module_real_logout_user (GckModule *base, CK_SLOT_ID slot_id)
+{
+	GckUserModule *self = GCK_USER_MODULE (base);
+	
+	if (!g_hash_table_remove (self->logged_in_apps, &slot_id))
+		return CKR_USER_NOT_LOGGED_IN;
+	
+	if (g_hash_table_size (self->logged_in_apps) > 0)
+		return CKR_OK;
+		
+	return gck_user_storage_lock (self->storage);
+}
+
+static GObject* 
+gck_user_module_constructor (GType type, guint n_props, GObjectConstructParam *props) 
+{
+	GckUserModule *self = GCK_USER_MODULE (G_OBJECT_CLASS (gck_user_module_parent_class)->constructor(type, n_props, props));	
+	g_return_val_if_fail (self, NULL);	
+
+	if (!self->directory)
+		self->directory = g_build_filename (g_get_home_dir (), ".gnome2", "keyrings", NULL);
+	self->storage = gck_user_storage_new (gck_module_get_manager (GCK_MODULE (self)), self->directory);
+
+	return G_OBJECT (self);
+}
+
+static void
+gck_user_module_init (GckUserModule *self)
+{
+	self->logged_in_apps = g_hash_table_new_full (gck_util_ulong_hash, gck_util_ulong_equal, gck_util_ulong_free, NULL);
+}
+
+static void
+gck_user_module_dispose (GObject *obj)
+{
+	GckUserModule *self = GCK_USER_MODULE (obj);
+	
+	if (self->storage)
+		g_object_unref (self->storage);
+	self->storage = NULL;
+	
+	g_hash_table_remove_all (self->logged_in_apps);
+    
+	G_OBJECT_CLASS (gck_user_module_parent_class)->dispose (obj);
+}
+
+static void
+gck_user_module_finalize (GObject *obj)
+{
+	GckUserModule *self = GCK_USER_MODULE (obj);
+	
+	g_assert (self->storage == NULL);
+	
+	g_assert (self->logged_in_apps);
+	g_hash_table_destroy (self->logged_in_apps);
+	self->logged_in_apps = NULL;
+	
+	g_free (self->directory);
+	self->directory = NULL;
+
+	G_OBJECT_CLASS (gck_user_module_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_module_class_init (GckUserModuleClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckModuleClass *module_class = GCK_MODULE_CLASS (klass);
+	
+	gobject_class->constructor = gck_user_module_constructor;
+	gobject_class->dispose = gck_user_module_dispose;
+	gobject_class->finalize = gck_user_module_finalize;
+	
+	module_class->parse_argument = gck_user_module_real_parse_argument;
+	module_class->refresh_token = gck_user_module_real_refresh_token;
+	module_class->store_token_object = gck_user_module_real_store_token_object;
+	module_class->remove_token_object = gck_user_module_real_remove_token_object;
+	module_class->login_user = gck_user_module_real_login_user;
+	module_class->logout_user = gck_user_module_real_logout_user;
+	module_class->login_change = gck_user_module_real_login_change;
+	
+	module_class->slot_info = &gck_user_module_slot_info;
+	module_class->token_info = &gck_user_module_token_info;
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+CK_FUNCTION_LIST_PTR
+gck_user_store_get_functions (void)
+{
+	gck_crypto_initialize ();
+	return gck_user_module_function_list;
+}

Added: trunk/pkcs11/user-store/gck-user-module.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-module.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,45 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_USER_MODULE_H__
+#define __GCK_USER_MODULE_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-module.h"
+
+#define GCK_TYPE_USER_MODULE               (gck_user_module_get_type ())
+#define GCK_USER_MODULE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_MODULE, GckUserModule))
+#define GCK_USER_MODULE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_MODULE, GckUserModuleClass))
+#define GCK_IS_USER_MODULE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_MODULE))
+#define GCK_IS_USER_MODULE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_MODULE))
+#define GCK_USER_MODULE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_MODULE, GckUserModuleClass))
+
+typedef struct _GckUserModule GckUserModule;
+typedef struct _GckUserModuleClass GckUserModuleClass;
+    
+struct _GckUserModuleClass {
+	GckModuleClass parent_class;
+};
+
+GType               gck_user_module_get_type               (void);
+
+#endif /* __GCK_USER_MODULE_H__ */

Added: trunk/pkcs11/user-store/gck-user-private-key.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-private-key.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,289 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-user-private-key.h"
+
+#include "gck/gck-attributes.h"
+#include "gck/gck-crypto.h"
+#include "gck/gck-data-der.h"
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-object.h"
+#include "gck/gck-serializable.h"
+#include "gck/gck-sexp.h"
+#include "gck/gck-util.h"
+
+#include <glib/gi18n.h>
+
+enum {
+	PROP_0,
+};
+
+struct _GckUserPrivateKey {
+	GckPrivateKey parent;
+	
+	guchar *private_data;
+	gsize n_private_data;
+	
+	GckSexp *private_sexp;
+	gboolean is_encrypted;
+	GckLogin *login;
+};
+
+static void gck_user_private_key_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckUserPrivateKey, gck_user_private_key, GCK_TYPE_PRIVATE_KEY, 0,
+               G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_user_private_key_serializable));
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL 
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static CK_RV
+gck_user_private_key_real_get_attribute (GckObject *base, CK_ATTRIBUTE_PTR attr)
+{
+	switch (attr->type) {
+	case CKA_ALWAYS_AUTHENTICATE:
+		return gck_attribute_set_bool (attr, FALSE);
+	}
+	
+	return GCK_OBJECT_CLASS (gck_user_private_key_parent_class)->get_attribute (base, attr);
+}
+
+static GckSexp* 
+gck_user_private_key_real_acquire_crypto_sexp (GckKey *base)
+{
+	GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
+	gcry_sexp_t sexp;
+	GckDataResult res;
+	const gchar *password;
+	gsize n_password;
+	
+	/* Non encrypted case */
+	if (self->private_sexp)
+		return gck_sexp_ref (self->private_sexp);
+
+	g_return_val_if_fail (self->login, NULL);
+	g_return_val_if_fail (self->is_encrypted, NULL);
+	
+	password = gck_login_get_password (self->login, &n_password);
+	res = gck_data_der_read_private_pkcs8 (self->private_data, self->n_private_data, 
+	                                       password, n_password, &sexp);
+	g_return_val_if_fail (res == GCK_DATA_SUCCESS, NULL);
+	
+	return gck_sexp_new (sexp);
+}
+
+static void
+gck_user_private_key_init (GckUserPrivateKey *self)
+{
+	
+}
+
+static void
+gck_user_private_key_dispose (GObject *obj)
+{
+	GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (obj);
+	
+	if (self->login)
+		g_object_unref (self->login);
+	self->login = NULL;
+    
+	G_OBJECT_CLASS (gck_user_private_key_parent_class)->dispose (obj);
+}
+
+static void
+gck_user_private_key_finalize (GObject *obj)
+{
+	GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (obj);
+	
+	g_assert (self->login == NULL);
+	
+	g_free (self->private_data);
+	self->private_data = NULL;
+	
+	if (self->private_sexp)
+		gck_sexp_unref (self->private_sexp);
+	self->private_sexp = NULL;
+	
+	G_OBJECT_CLASS (gck_user_private_key_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_private_key_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                           GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_user_private_key_get_property (GObject *obj, guint prop_id, GValue *value, 
+                           GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_user_private_key_class_init (GckUserPrivateKeyClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckObjectClass *gck_class = GCK_OBJECT_CLASS (klass);
+	GckKeyClass *key_class = GCK_KEY_CLASS (klass);
+	
+	gobject_class->dispose = gck_user_private_key_dispose;
+	gobject_class->finalize = gck_user_private_key_finalize;
+	gobject_class->set_property = gck_user_private_key_set_property;
+	gobject_class->get_property = gck_user_private_key_get_property;
+	
+	gck_class->get_attribute = gck_user_private_key_real_get_attribute;
+	
+	key_class->acquire_crypto_sexp = gck_user_private_key_real_acquire_crypto_sexp;
+}
+
+static gboolean
+gck_user_private_key_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
+{
+	GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
+	GckDataResult res;
+	gcry_sexp_t sexp, pub;
+	GckSexp *wrapper;
+	const gchar *password;
+	gsize n_password;
+
+	g_return_val_if_fail (GCK_IS_USER_PRIVATE_KEY (self), FALSE);
+	g_return_val_if_fail (data, FALSE);
+	
+	res = gck_data_der_read_private_pkcs8 (data, n_data, NULL, 0, &sexp);
+	
+	/* An unencrypted pkcs8 file */
+	if (res == GCK_DATA_SUCCESS) {
+		self->is_encrypted = FALSE;
+	
+	/* If it's locked, then use our token password */
+	} else if (res == GCK_DATA_LOCKED) {
+		self->is_encrypted = TRUE;
+		
+		if (!login) {
+			g_message ("encountered private key but no private key present");
+			return FALSE;
+		}
+	
+		password = gck_login_get_password (login, &n_password);
+		res = gck_data_der_read_private_pkcs8 (data, n_data, password, n_password, &sexp);
+	}
+
+	switch (res) {
+	case GCK_DATA_LOCKED:
+		g_message ("private key is encrypted with wrong password");
+		return FALSE;
+	case GCK_DATA_FAILURE:
+		g_message ("couldn't parse private key");
+		return FALSE;
+	case GCK_DATA_UNRECOGNIZED:
+		g_message ("invalid or unrecognized private key");
+		return FALSE;
+	case GCK_DATA_SUCCESS:
+		break;
+	default:
+		g_assert_not_reached();
+	}
+	
+	/* Calculate a public key as our 'base' */
+	if (!gck_crypto_sexp_key_to_public (sexp, &pub))
+		g_return_val_if_reached (FALSE);
+	
+	/* Keep the public part of the key around for answering queries */
+	wrapper = gck_sexp_new (pub);
+	gck_key_set_base_sexp (GCK_KEY (self), wrapper);
+	gck_sexp_unref (wrapper);
+	
+	/* Encrypted private key, keep login and data */
+	if (self->is_encrypted) {
+		g_free (self->private_data);
+		self->n_private_data = n_data;
+		self->private_data = g_memdup (data, n_data);
+		g_object_set (self, "login", login, NULL);
+
+		/* Don't need the private key any more */
+		gcry_sexp_release (sexp);
+
+	/* Not encrypted, just keep the parsed key */
+	} else {
+		wrapper = gck_sexp_new (sexp);
+		if (self->private_sexp)
+			gck_sexp_unref (self->private_sexp);
+		self->private_sexp = wrapper;
+	}
+	
+	return TRUE;
+}
+
+static gboolean 
+gck_user_private_key_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+	GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
+	const gchar *password;
+	gsize n_password;
+	GckSexp *sexp;
+	
+	g_return_val_if_fail (GCK_IS_USER_PRIVATE_KEY (self), FALSE);
+	g_return_val_if_fail (data, FALSE);
+	g_return_val_if_fail (n_data, FALSE);
+	
+	sexp = gck_user_private_key_real_acquire_crypto_sexp (GCK_KEY (self));
+	g_return_val_if_fail (sexp, FALSE);
+	
+	password = gck_login_get_password (login, &n_password);
+	if (password == NULL) 
+		*data = gck_data_der_write_private_pkcs8_plain (gck_sexp_get (sexp), n_data);
+	else
+		*data = gck_data_der_write_private_pkcs8_crypted (gck_sexp_get (sexp), password,
+		                                                  n_password, n_data);
+	
+	gck_sexp_unref (sexp);
+	return *data != NULL;
+}
+
+static void 
+gck_user_private_key_serializable (GckSerializableIface *iface)
+{
+	iface->extension = ".pkcs8";
+	iface->load = gck_user_private_key_real_load;
+	iface->save = gck_user_private_key_real_save;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */

Added: trunk/pkcs11/user-store/gck-user-private-key.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-private-key.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,48 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_USER_PRIVATE_KEY_H__
+#define __GCK_USER_PRIVATE_KEY_H__
+
+#include <glib-object.h>
+
+#include "gck-user-private-key.h"
+
+#include "gck/gck-login.h"
+#include "gck/gck-private-key.h"
+
+#define GCK_TYPE_USER_PRIVATE_KEY               (gck_user_private_key_get_type ())
+#define GCK_USER_PRIVATE_KEY(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_PRIVATE_KEY, GckUserPrivateKey))
+#define GCK_USER_PRIVATE_KEY_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_PRIVATE_KEY, GckUserPrivateKeyClass))
+#define GCK_IS_USER_PRIVATE_KEY(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_PRIVATE_KEY))
+#define GCK_IS_USER_PRIVATE_KEY_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_PRIVATE_KEY))
+#define GCK_USER_PRIVATE_KEY_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_PRIVATE_KEY, GckUserPrivateKeyClass))
+
+typedef struct _GckUserPrivateKey GckUserPrivateKey;
+typedef struct _GckUserPrivateKeyClass GckUserPrivateKeyClass;
+    
+struct _GckUserPrivateKeyClass {
+	GckPrivateKeyClass parent_class;
+};
+
+GType               gck_user_private_key_get_type               (void);
+
+#endif /* __GCK_USER_PRIVATE_KEY_H__ */

Added: trunk/pkcs11/user-store/gck-user-public-key.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-public-key.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,157 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-user-public-key.h"
+
+#include "gck/gck-attributes.h"
+#include "gck/gck-data-der.h"
+#include "gck/gck-serializable.h"
+#include "gck/gck-object.h"
+#include "gck/gck-util.h"
+
+#include <glib/gi18n.h>
+
+struct _GckUserPublicKey {
+	GckPublicKey parent;
+};
+
+static void gck_user_public_key_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckUserPublicKey, gck_user_public_key, GCK_TYPE_PUBLIC_KEY, 0,
+               G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_user_public_key_serializable));
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static void
+gck_user_public_key_init (GckUserPublicKey *self)
+{
+	
+}
+
+static void
+gck_user_public_key_finalize (GObject *obj)
+{
+	/* GckUserPublicKey *self = GCK_USER_PUBLIC_KEY (obj); */
+	G_OBJECT_CLASS (gck_user_public_key_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_public_key_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                                  GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_user_public_key_get_property (GObject *obj, guint prop_id, GValue *value, 
+                                  GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_user_public_key_class_init (GckUserPublicKeyClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    
+	gobject_class->finalize = gck_user_public_key_finalize;
+	gobject_class->set_property = gck_user_public_key_set_property;
+	gobject_class->get_property = gck_user_public_key_get_property;
+}
+
+
+static gboolean
+gck_user_public_key_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
+{
+	GckUserPublicKey *self = GCK_USER_PUBLIC_KEY (base);
+	GckDataResult res;
+	GckSexp *wrapper;
+	gcry_sexp_t sexp;
+	
+	g_return_val_if_fail (GCK_IS_USER_PUBLIC_KEY (self), FALSE);
+	g_return_val_if_fail (data, FALSE);
+	
+	res = gck_data_der_read_public_key (data, n_data, &sexp);
+	
+	switch (res) {
+	case GCK_DATA_LOCKED:
+		g_message ("public key is locked");
+		return FALSE;
+	case GCK_DATA_FAILURE:
+		g_message ("couldn't parse public key");
+		return FALSE;
+	case GCK_DATA_UNRECOGNIZED:
+		g_message ("invalid or unrecognized public key");
+		return FALSE;
+	case GCK_DATA_SUCCESS:
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	wrapper = gck_sexp_new (sexp);
+	gck_key_set_base_sexp (GCK_KEY (self), wrapper);
+	gck_sexp_unref (wrapper);
+	
+	return TRUE;
+}
+
+static gboolean 
+gck_user_public_key_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+	GckUserPublicKey *self = GCK_USER_PUBLIC_KEY (base);
+	GckSexp *wrapper;
+
+	g_return_val_if_fail (GCK_IS_USER_PUBLIC_KEY (self), FALSE);
+	g_return_val_if_fail (data, FALSE);
+	g_return_val_if_fail (n_data, FALSE);
+
+	wrapper = gck_key_get_base_sexp (GCK_KEY (self));
+	g_return_val_if_fail (wrapper, FALSE);
+	
+	*data = gck_data_der_write_public_key (gck_sexp_get (wrapper), n_data);
+	return *data != NULL;
+}
+
+static void 
+gck_user_public_key_serializable (GckSerializableIface *iface)
+{
+	iface->extension = ".pub";
+	iface->load = gck_user_public_key_real_load;
+	iface->save = gck_user_public_key_real_save;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+

Added: trunk/pkcs11/user-store/gck-user-public-key.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-public-key.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,47 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_USER_PUBLIC_KEY_H__
+#define __GCK_USER_PUBLIC_KEY_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-public-key.h"
+
+#define GCK_TYPE_USER_PUBLIC_KEY               (gck_user_public_key_get_type ())
+#define GCK_USER_PUBLIC_KEY(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_PUBLIC_KEY, GckUserPublicKey))
+#define GCK_USER_PUBLIC_KEY_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_PUBLIC_KEY, GckUserPublicKeyClass))
+#define GCK_IS_USER_PUBLIC_KEY(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_PUBLIC_KEY))
+#define GCK_IS_USER_PUBLIC_KEY_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_PUBLIC_KEY))
+#define GCK_USER_PUBLIC_KEY_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_PUBLIC_KEY, GckUserPublicKeyClass))
+
+typedef struct _GckUserPublicKey GckUserPublicKey;
+typedef struct _GckUserPublicKeyClass GckUserPublicKeyClass;
+    
+struct _GckUserPublicKeyClass {
+	GckPublicKeyClass parent_class;
+};
+
+GType                gck_user_public_key_get_type               (void);
+
+GckUserPublicKey*    gck_user_public_key_new                    (const gchar *unique);
+
+#endif /* __GCK_USER_PUBLIC_KEY_H__ */

Added: trunk/pkcs11/user-store/gck-user-standalone.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-standalone.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,57 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-user-standalone.h - The user-store PKCS#11 code as a standalone module
+
+   Copyright (C) 2008, Stef 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 <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-user-store.h"
+
+#include "gck/gck-crypto.h"
+
+#include "common/gkr-secure-memory.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <glib-object.h>
+
+/* Module callbacks for secure memory */
+static GStaticMutex memory_mutex = G_STATIC_MUTEX_INIT;
+void gkr_memory_lock (void) 
+	{ g_static_mutex_lock (&memory_mutex); }
+void gkr_memory_unlock (void) 
+	{ g_static_mutex_unlock (&memory_mutex); }
+void* gkr_memory_fallback (void *p, unsigned long sz) 
+	{ return g_realloc (p, sz); }
+
+CK_RV
+C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+	if (!list)
+		return CKR_ARGUMENTS_BAD;
+	
+	g_type_init ();
+	if (!g_thread_supported ())
+		g_thread_init (NULL);
+	
+	*list = gck_user_store_get_functions ();
+	return CKR_OK;
+}

Added: trunk/pkcs11/user-store/gck-user-storage.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-storage.c	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,1228 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#include "config.h"
+
+#include "gck-user-storage.h"
+#include "gck-user-private-key.h"
+#include "gck-user-public-key.h"
+
+#include "gck/gck-certificate.h"
+#include "gck/gck-data-asn1.h"
+#include "gck/gck-data-file.h"
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-serializable.h"
+#include "gck/gck-util.h"
+
+#include <glib/gstdio.h>
+
+#include <libtasn1.h>
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+enum {
+	PROP_0,
+	PROP_DIRECTORY,
+	PROP_MANAGER,
+	PROP_LOGIN
+};
+
+struct _GckUserStorage {
+	GckStore parent;
+
+	GckManager *manager;
+
+	/* Information about file data */
+	gchar *directory;
+	gchar *filename;
+	GckDataFile *file;
+	time_t last_mtime;
+	GckLogin *login;
+	
+	/* Mapping of objects loaded */
+	GHashTable *object_to_identifier;
+	GHashTable *identifier_to_object;
+	
+	/* Valid when in write state */
+	GckTransaction *transaction;
+	gchar *write_path;
+	gint write_fd;
+	gint read_fd;
+};
+
+G_DEFINE_TYPE (GckUserStorage, gck_user_storage, GCK_TYPE_STORE);
+
+#define MAX_LOCK_TRIES 20
+
+#define UNWANTED_IDENTIFIER_CHARS  ":/\\<>|\t\n\r\v "
+
+/* -----------------------------------------------------------------------------
+ * HELPERS 
+ */
+
+static void
+dispose_unref_object (gpointer obj)
+{
+	g_assert (G_IS_OBJECT (obj));
+	g_object_run_dispose (obj);
+	g_object_unref (obj);
+}
+
+#ifndef HAVE_FLOCK
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+	
+static int flock(int fd, int operation)
+{
+	struct flock flock;
+	
+	switch (operation & ~LOCK_NB) {
+	case LOCK_SH:
+		flock.l_type = F_RDLCK;
+		break;
+	case LOCK_EX:
+		flock.l_type = F_WRLCK;
+		break;
+	case LOCK_UN:
+		flock.l_type = F_UNLCK;
+		break;
+	default:
+		errno = EINVAL;
+		return -1;
+	}
+	
+	flock.l_whence = 0;
+	flock.l_start = 0;
+	flock.l_len = 0;
+	
+	return fcntl (fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &flock);
+}
+#endif /* !HAVE_FLOCK */ 
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL 
+ */
+
+
+static gchar*
+name_for_subject (const guchar *subject, gsize n_subject)
+{
+	ASN1_TYPE asn;
+	gchar *name;
+	
+	g_assert (subject);
+	g_assert (n_subject);
+	
+	asn = gck_data_asn1_decode ("PKIX1.Name", subject, n_subject);
+	g_return_val_if_fail (asn, NULL);
+	
+	name = gck_data_asn1_read_dn_part (asn, "rdnSequence", "CN");
+	asn1_delete_structure (&asn);
+	
+	return name;
+}
+
+static gchar*
+identifier_for_object (GckObject *object)
+{
+	GckSerializableIface *serial;
+	const gchar *ext;
+	gchar *identifier;
+	gchar *name = NULL;
+	guchar *data;
+	gsize n_data;
+	
+	g_assert (GCK_IS_OBJECT (object));
+	g_assert (GCK_IS_SERIALIZABLE (object));
+	
+	/* Figure out the extension and prefix */
+	serial = GCK_SERIALIZABLE_GET_INTERFACE (object);
+	ext = serial->extension;
+	g_return_val_if_fail (ext, NULL);
+	
+	/* First we try to use the CN of a subject */
+	data = gck_object_get_attribute_data (object, CKA_SUBJECT, &n_data);
+	if (data && n_data) 
+		name = name_for_subject (data, n_data);
+	g_free (data);
+	
+	/* Next we try hex encoding the ID */
+	if (name == NULL) {
+		data = gck_object_get_attribute_data (object, CKA_ID, &n_data);
+		if (data && n_data)
+			name = gck_util_hex_encode (data, n_data);
+		g_free (data);
+	}
+	
+	/* Build up the identifier */
+	identifier = g_strconcat (name ? "-" : "", name, ext, NULL);
+	g_strdelimit (identifier, UNWANTED_IDENTIFIER_CHARS, '_');
+
+	g_free (name);
+	return identifier;
+}
+
+static GType
+type_from_extension (const gchar *extension)
+{
+	g_assert (extension);
+	
+	if (strcmp (extension, ".pkcs8") == 0)
+		return GCK_TYPE_USER_PRIVATE_KEY;
+	else if (strcmp (extension, ".pub") == 0)
+		return GCK_TYPE_USER_PUBLIC_KEY;
+	else if (strcmp (extension, ".cer") == 0)
+		return GCK_TYPE_CERTIFICATE;
+	
+	return 0;
+}
+
+
+static GType
+type_from_identifier (const gchar *identifier)
+{
+	const gchar *ext;
+	
+	g_assert (identifier);
+	
+	ext = strrchr (identifier, '.');
+	if (ext == NULL)
+		return 0;
+	
+	return type_from_extension (ext);
+}
+
+static gboolean
+complete_lock_file (GckTransaction *transaction, GObject *object, gpointer data)
+{
+	int fd = GPOINTER_TO_INT (data);
+	
+	/* This also unlocks the file */
+	close (fd);
+	
+	/* Completed successfully */
+	return TRUE;
+}
+
+static gint
+begin_lock_file (GckUserStorage *self, GckTransaction *transaction)
+{
+	guint tries = 0;
+	gint fd = -1;
+	
+	/* 
+	 * In this function we don't actually put the object into a 'write' state,
+	 * that's the callers job if necessary.
+	 */ 
+
+	g_assert (GCK_IS_USER_STORAGE (self));
+	g_assert (GCK_IS_TRANSACTION (transaction));
+
+	g_return_val_if_fail (!gck_transaction_get_failed (transaction), -1);
+
+	/* File lock retry loop */
+	for (tries = 0; TRUE; ++tries) {
+		if (tries > MAX_LOCK_TRIES) {
+			g_message ("couldn't write to store file: %s: file is locked", self->filename);
+			gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+			return -1;
+		}
+
+		fd = open (self->filename, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+		if (fd == -1) {
+			g_message ("couldn't open store file: %s: %s", self->filename, g_strerror (errno));
+			gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+			return -1;
+		}
+	
+		if (flock (fd, LOCK_EX | LOCK_NB) < 0) {
+			if (errno != EWOULDBLOCK) {
+				g_message ("couldn't lock store file: %s: %s", self->filename, g_strerror (errno));
+				close (fd);
+				gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+				return -1;
+			}
+				
+			close (fd);
+			fd = -1;
+			g_usleep (200000);
+			continue;
+		}
+
+		/* Successfully opened file */;
+		gck_transaction_add (transaction, self, complete_lock_file, GINT_TO_POINTER (fd));
+		return fd;
+	}
+
+	g_assert_not_reached ();
+}
+
+static gboolean
+complete_write_state (GckTransaction *transaction, GObject *object, gpointer unused)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (object);
+	gboolean ret = TRUE;
+	struct stat sb;
+	
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (object), FALSE);
+	g_return_val_if_fail (GCK_IS_TRANSACTION (transaction), FALSE);
+	g_return_val_if_fail (self->transaction == transaction, FALSE);
+	
+	/* Transaction succeeded, overwrite the old with the new */
+	if (!gck_transaction_get_failed (transaction)) {
+
+		if (g_rename (self->write_path, self->filename) == -1) {
+			g_warning ("couldn't rename temporary store file: %s", self->write_path);
+			ret = FALSE;
+		} else {
+			if (fstat (self->write_fd, &sb) >= 0)
+				self->last_mtime = sb.st_mtime;
+		}
+	} 
+	
+	/* read_fd is closed by complete_lock_file */
+	
+	if (self->write_fd != -1)
+		close (self->write_fd);
+	self->write_fd = -1;
+	
+	g_free (self->write_path);
+	self->write_path = NULL;
+
+	g_object_unref (self->transaction);
+	self->transaction = NULL;
+	
+	return ret;
+}
+
+static gboolean
+begin_write_state (GckUserStorage *self, GckTransaction *transaction)
+{
+	g_assert (GCK_IS_USER_STORAGE (self));
+	g_assert (GCK_IS_TRANSACTION (transaction));
+
+	g_return_val_if_fail (!gck_transaction_get_failed (transaction), FALSE);
+	
+	/* Already in write state for this transaction? */
+	if (self->transaction != NULL) {
+		g_return_val_if_fail (self->transaction == transaction, FALSE);
+		return TRUE;
+	}
+	
+	gck_transaction_add (transaction, self, complete_write_state, NULL);
+	self->transaction = g_object_ref (transaction);
+	
+	/* Lock file for the transaction */
+	self->read_fd = begin_lock_file (self, transaction);
+	if (self->read_fd == -1)
+		return FALSE;
+
+	/* Open the new file */
+	g_assert (self->write_fd == -1);
+	self->write_path = g_strdup_printf ("%s.XXXXXX", self->filename);
+	self->write_fd = g_mkstemp (self->write_path);
+	if (self->write_fd == -1) {
+		g_message ("couldn't open new temporary store file: %s: %s", self->write_path, g_strerror (errno));
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+static gboolean
+begin_write_state_and_refresh (GckUserStorage *self, GckTransaction *transaction)
+{
+	GckDataResult res;
+	struct stat sb;
+	CK_RV rv;
+	
+	if (!begin_write_state (self, transaction))
+		return FALSE;
+	
+	/* See if file needs updating */
+	if (fstat (self->read_fd, &sb) >= 0 && sb.st_mtime != self->last_mtime) {
+		
+		res = gck_data_file_read_fd (self->file, self->read_fd, self->login);
+		switch (res) {
+		case GCK_DATA_FAILURE:
+			g_message ("failure updating user store file: %s", self->filename);
+			rv = CKR_FUNCTION_FAILED;
+			break;
+		case GCK_DATA_LOCKED:
+			rv = CKR_USER_NOT_LOGGED_IN;
+			break;
+		case GCK_DATA_UNRECOGNIZED:
+			g_message ("unrecognized or invalid user store file: %s", self->filename);
+			rv = CKR_FUNCTION_FAILED;
+			break;
+		case GCK_DATA_SUCCESS:
+			rv = CKR_OK;
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+		
+		if (rv != CKR_OK) {
+			gck_transaction_fail (transaction, rv);
+			return FALSE;
+		}
+	}
+	
+	return TRUE;
+}
+
+static void
+take_object_ownership (GckUserStorage *self, const gchar *identifier, GckObject *object)
+{
+	gchar *str;
+	
+	g_assert (GCK_IS_USER_STORAGE (self));
+	g_assert (GCK_IS_OBJECT (object));
+	
+	g_assert (g_hash_table_lookup (self->identifier_to_object, identifier) == NULL);
+	g_assert (g_hash_table_lookup (self->object_to_identifier, object) == NULL);
+	
+	str = g_strdup (identifier);
+	object = g_object_ref (object);
+	
+	g_hash_table_replace (self->identifier_to_object, str, object);
+	g_hash_table_replace (self->object_to_identifier, object, str);;
+	
+	g_object_set (object, "store", self, NULL);
+	gck_manager_register_object (self->manager, object);
+}
+
+static void 
+data_file_entry_added (GckDataFile *store, const gchar *identifier, GckUserStorage *self)
+{
+	GError *error = NULL;
+	GckObject *object;
+	GckDataResult res;
+	gboolean ret;
+	guchar *data;
+	gsize n_data;
+	GType type;
+	gchar *path;
+
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (identifier);
+
+	/* Already have this object? */
+	object = g_hash_table_lookup (self->identifier_to_object, identifier);
+	if (object != NULL)
+		return;
+
+	/* Figure out what type of object we're dealing with */
+	type = type_from_identifier (identifier);
+	if (type == 0) {
+		g_warning ("don't know how to load file in user store: %s", identifier);
+		return;
+	}
+	
+	/* Read the file in */
+	path = g_build_filename (self->directory, identifier, NULL);
+	ret = g_file_get_contents (path, (gchar**)&data, &n_data, &error);
+	g_free (path);
+	
+	if (ret == FALSE) {
+		g_warning ("couldn't read file in user store: %s: %s", identifier, 
+		           error && error->message ? error->message : "");
+		g_clear_error (&error);
+		return;
+	}
+	
+	/* Create a new object for this identifier */
+	object = g_object_new (type, "unique", identifier, NULL);
+	g_return_if_fail (GCK_IS_SERIALIZABLE (object));
+	g_return_if_fail (GCK_SERIALIZABLE_GET_INTERFACE (object)->extension);
+
+	/* And load the data into it */
+	res = gck_serializable_load (GCK_SERIALIZABLE (object), self->login, data, n_data);
+	g_free (data);
+	
+	switch (res) {
+	case GCK_DATA_FAILURE:
+		g_message ("failed to load file in user store: %s", identifier);
+		return;
+	case GCK_DATA_UNRECOGNIZED:
+		g_message ("invalid or unparsable file in user store: %s", identifier);
+		return;
+	case GCK_DATA_LOCKED:
+		g_message ("file is locked with unknown password: %s", identifier);
+		return;
+	case GCK_DATA_SUCCESS:
+		take_object_ownership (self, identifier, object);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	g_object_unref (object);
+}
+
+static void 
+data_file_entry_changed (GckDataFile *store, const gchar *identifier, CK_ATTRIBUTE_TYPE type, GckUserStorage *self)
+{
+	GckObject *object;
+	
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (identifier);
+	
+	object = g_hash_table_lookup (self->identifier_to_object, identifier);
+	if (object != NULL)
+		gck_object_notify_attribute (object, type);
+}
+
+static void 
+data_file_entry_removed (GckDataFile *store, const gchar *identifier, GckUserStorage *self)
+{
+	GckObject *object;
+		
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (identifier);
+	
+	object = g_hash_table_lookup (self->identifier_to_object, identifier);
+	if (object != NULL) {
+		g_object_set (object, "store", NULL, NULL);
+
+		/* Unrefs and also disposes the object, which unregisters from manager*/
+		g_hash_table_remove (self->identifier_to_object, identifier);
+		g_hash_table_remove (self->object_to_identifier, object);
+	}
+}
+
+static void
+relock_object (GckTransaction *transaction, const gchar *path, 
+               const gchar *identifier, GckLogin *old_login, GckLogin *new_login)
+{
+	GError *error = NULL;
+	GckObject *object;
+	GckDataResult res;
+	guchar *data;
+	gsize n_data;
+	GType type;
+	CK_RV rv;
+	
+	g_assert (GCK_IS_TRANSACTION (transaction));
+	g_assert (identifier);
+	g_assert (path);
+	
+	g_assert (!gck_transaction_get_failed (transaction));
+
+	/* Figure out the type of object */
+	type = type_from_identifier (identifier);
+	if (type == 0) {
+		g_warning ("don't know how to relock file in user store: %s", identifier);
+		gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+		return;
+	}
+	
+	/* Create a dummy object for this identifier */
+	object = g_object_new (type, "unique", identifier, NULL);
+	if (!GCK_IS_SERIALIZABLE (object)) {
+		g_warning ("cannot relock unserializable object for file in user store: %s", identifier);
+		gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+		return;
+	}
+	
+	/* Read in the data for the object */
+	if (!g_file_get_contents (path, (gchar**)&data, &n_data, &error)) {
+		g_message ("couldn't relock file in user store: %s: %s", identifier,
+		           error && error->message ? error->message : "");
+		g_clear_error (&error);
+		g_object_unref (object);
+		return;
+	}
+	
+	/* Load it into our temporary object */
+	res = gck_serializable_load (GCK_SERIALIZABLE (object), old_login, data, n_data);
+	g_free (data);
+	
+	switch (res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		g_message ("unrecognized or invalid user store file: %s", identifier);
+		rv = CKR_FUNCTION_FAILED;
+		break;
+	case GCK_DATA_LOCKED:
+		g_message ("old login is invalid for user store file: %s", identifier);
+		rv = CKR_PIN_INCORRECT;
+		break;
+	case GCK_DATA_SUCCESS:
+		rv = CKR_OK;
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	if (rv != CKR_OK) {
+		gck_transaction_fail (transaction, rv);
+		g_object_unref (object);
+		return;
+	}
+		
+	/* Read it out of our temporary object */
+	res = gck_serializable_save (GCK_SERIALIZABLE (object), new_login, &data, &n_data);
+	g_object_unref (object);
+	
+	switch (res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		g_warning ("unable to serialize data with new login: %s", identifier);
+		rv = CKR_GENERAL_ERROR;
+		break;
+	case GCK_DATA_LOCKED:
+		g_message ("new login is invalid for user store file: %s", identifier);
+		rv = CKR_PIN_INVALID;
+		break;
+	case GCK_DATA_SUCCESS:
+		rv = CKR_OK;
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	if (rv != CKR_OK) {
+		gck_transaction_fail (transaction, rv);
+		g_free (data);
+		return;
+	}
+	
+	/* And write it back out to the file */
+	gck_transaction_write_file (transaction, path, data, n_data);
+	g_free (data);
+}
+
+typedef struct _RelockArgs {
+	GckUserStorage *self;
+	GckTransaction *transaction;
+	GckLogin *old_login;
+	GckLogin *new_login;
+} RelockArgs;
+
+static void
+relock_each_object (GckDataFile *file, const gchar *identifier, gpointer data)
+{
+	RelockArgs *args = data;
+	gchar *path;
+	guint section;
+	
+	g_assert (GCK_IS_USER_STORAGE (args->self));
+	if (gck_transaction_get_failed (args->transaction))
+		return;
+	
+	if (!gck_data_file_lookup_entry (file, identifier, &section))
+		g_return_if_reached ();
+
+	/* Only operate on private files */
+	if (section != GCK_DATA_FILE_SECTION_PRIVATE)
+		return;
+
+	path = g_build_filename (args->self->directory, identifier, NULL);
+	relock_object (args->transaction, path, identifier, args->old_login, args->new_login);
+	g_free (path);
+}
+
+static CK_RV
+refresh_with_login (GckUserStorage *self, GckLogin *login)
+{
+	GckDataResult res;
+	struct stat sb;
+	CK_RV rv;
+	int fd;
+	
+	g_assert (GCK_USER_STORAGE (self));
+	
+	/* Open the file for reading */
+	fd = open (self->filename, O_RDONLY, 0);
+	if (fd == -1) {
+		/* No file, no worries */
+		if (errno == ENOENT)
+			return login ? CKR_USER_PIN_NOT_INITIALIZED : CKR_OK;
+		g_message ("couldn't open store file: %s: %s", self->filename, g_strerror (errno));
+		return CKR_FUNCTION_FAILED;
+	}
+
+	/* Try and update the last read time */
+	if (fstat (fd, &sb) >= 0) 
+		self->last_mtime = sb.st_mtime;
+	
+	res = gck_data_file_read_fd (self->file, fd, login);
+	switch (res) {
+	case GCK_DATA_FAILURE:
+		g_message ("failure reading from file: %s", self->filename);
+		rv = CKR_FUNCTION_FAILED;
+		break;
+	case GCK_DATA_LOCKED:
+		rv = CKR_USER_NOT_LOGGED_IN;
+		break;
+	case GCK_DATA_UNRECOGNIZED:
+		g_message ("unrecognized or invalid user store file: %s", self->filename);
+		rv = CKR_FUNCTION_FAILED;
+		break;
+	case GCK_DATA_SUCCESS:
+		rv = CKR_OK;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+	
+	/* Force a reread on next write */
+	if (rv == CKR_FUNCTION_FAILED)
+		self->last_mtime = 0;
+	
+	close (fd);
+	return rv;
+}
+
+static void
+set_storage_login (GckUserStorage *self, GckLogin *login)
+{
+	g_assert (GCK_IS_USER_STORAGE (self));
+	if (login != NULL)
+		g_object_ref (login);
+	if (self->login)
+		g_object_unref (self->login);
+	self->login = login;
+	g_object_notify (G_OBJECT (self), "login");
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static CK_RV 
+gck_user_storage_real_read_value (GckStore *base, GckObject *object, CK_ATTRIBUTE_PTR attr)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (base);
+	const gchar *identifier;
+	GckDataResult res;
+	gconstpointer value;
+	gsize n_value;
+	CK_RV rv;
+	
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (self), CKR_GENERAL_ERROR);
+	g_return_val_if_fail (GCK_IS_OBJECT (object), CKR_GENERAL_ERROR);
+	g_return_val_if_fail (attr, CKR_GENERAL_ERROR);
+
+	identifier = g_hash_table_lookup (self->object_to_identifier, object);
+	if (!identifier)
+		return CKR_ATTRIBUTE_TYPE_INVALID;
+	
+	if (self->last_mtime == 0) {
+		rv = gck_user_storage_refresh (self);
+		if (rv != CKR_OK)
+			return rv;
+	}
+	
+	res = gck_data_file_read_value (self->file, identifier, attr->type, &value, &n_value);
+	switch (res) {
+	case GCK_DATA_FAILURE:
+		g_return_val_if_reached (CKR_GENERAL_ERROR);
+	case GCK_DATA_UNRECOGNIZED:
+		return CKR_ATTRIBUTE_TYPE_INVALID;
+	case GCK_DATA_LOCKED:
+		return CKR_USER_NOT_LOGGED_IN;
+	case GCK_DATA_SUCCESS:
+		/* Yes, we don't fill a buffer, just return pointer */
+		attr->pValue = (CK_VOID_PTR)value;
+		attr->ulValueLen = n_value;
+		return CKR_OK;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void 
+gck_user_storage_real_write_value (GckStore *base, GckTransaction *transaction, GckObject *object, CK_ATTRIBUTE_PTR attr)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (base);
+	const gchar *identifier;
+	GckDataResult res;
+	CK_RV rv;
+	
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (GCK_IS_OBJECT (object));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+	g_return_if_fail (attr);
+
+	identifier = g_hash_table_lookup (self->object_to_identifier, object);
+	if (!identifier) {
+		gck_transaction_fail (transaction, CKR_ATTRIBUTE_READ_ONLY);
+		return;
+	}
+	
+	if (self->last_mtime == 0) {
+		rv = gck_user_storage_refresh (self);
+		if (rv != CKR_OK) {
+			gck_transaction_fail (transaction, rv);
+			return;
+		}
+	}
+
+	res = gck_data_file_write_value (self->file, identifier, attr->type, attr->pValue, attr->ulValueLen);
+	switch (res) {
+	case GCK_DATA_FAILURE:
+		rv = CKR_FUNCTION_FAILED;
+		break;
+	case GCK_DATA_UNRECOGNIZED:
+		rv = CKR_ATTRIBUTE_READ_ONLY;
+		break;
+	case GCK_DATA_LOCKED:
+		rv = CKR_USER_NOT_LOGGED_IN;
+		break;
+	case GCK_DATA_SUCCESS:
+		rv = CKR_OK;
+		break;
+	default:
+		g_assert_not_reached ();
+	}	
+
+	if (rv != CKR_OK)
+		gck_transaction_fail (transaction, rv);
+}
+
+static GObject* 
+gck_user_storage_constructor (GType type, guint n_props, GObjectConstructParam *props) 
+{
+	GckUserStorage *self = GCK_USER_STORAGE (G_OBJECT_CLASS (gck_user_storage_parent_class)->constructor(type, n_props, props));
+	g_return_val_if_fail (self, NULL);	
+
+	g_return_val_if_fail (self->directory, NULL);
+	self->filename = g_build_filename (self->directory, "user.keystore", NULL);
+	
+	g_return_val_if_fail (self->manager, NULL);
+	
+	return G_OBJECT (self);
+}
+
+static void
+gck_user_storage_init (GckUserStorage *self)
+{
+	self->file = gck_data_file_new ();
+	g_signal_connect (self->file, "entry-added", G_CALLBACK (data_file_entry_added), self);
+	g_signal_connect (self->file, "entry-changed", G_CALLBACK (data_file_entry_changed), self);
+	g_signal_connect (self->file, "entry-removed", G_CALLBACK (data_file_entry_removed), self);
+	
+	/* Each one owns the key and contains weak ref to other's key as its value */
+	self->object_to_identifier = g_hash_table_new_full (g_direct_hash, g_direct_equal, dispose_unref_object, NULL);
+	self->identifier_to_object = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+static void
+gck_user_storage_dispose (GObject *obj)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (obj);
+	
+	if (self->manager)
+		g_object_unref (self->manager);
+	self->manager = NULL;
+	
+	g_signal_handlers_disconnect_by_func (self->file, data_file_entry_added, self);
+	g_signal_handlers_disconnect_by_func (self->file, data_file_entry_changed, self);
+	g_signal_handlers_disconnect_by_func (self->file, data_file_entry_removed, self);
+	
+	g_hash_table_remove_all (self->object_to_identifier);
+	g_hash_table_remove_all (self->identifier_to_object);
+	
+	G_OBJECT_CLASS (gck_user_storage_parent_class)->dispose (obj);
+}
+
+static void
+gck_user_storage_finalize (GObject *obj)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (obj);
+	
+	g_assert (self->file);
+	g_object_unref (self->file);
+	self->file = NULL;
+	
+	g_free (self->filename);
+	self->filename = NULL;
+	
+	g_assert (self->directory);
+	g_free (self->directory);
+	self->directory = NULL;
+	
+	g_assert (self->object_to_identifier);
+	g_hash_table_destroy (self->object_to_identifier);
+	g_hash_table_destroy (self->identifier_to_object);
+
+	G_OBJECT_CLASS (gck_user_storage_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_storage_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                           GParamSpec *pspec)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (obj);
+	
+	switch (prop_id) {
+	case PROP_DIRECTORY:
+		g_return_if_fail (!self->directory);
+		self->directory = g_value_dup_string (value);
+		g_return_if_fail (self->directory);
+		break;
+	case PROP_MANAGER:
+		g_return_if_fail (!self->manager);
+		self->manager = g_value_dup_object (value);
+		g_return_if_fail (self->manager);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_user_storage_get_property (GObject *obj, guint prop_id, GValue *value, 
+                               GParamSpec *pspec)
+{
+	GckUserStorage *self = GCK_USER_STORAGE (obj);
+	
+	switch (prop_id) {
+	case PROP_DIRECTORY:
+		g_value_set_string (value, gck_user_storage_get_directory (self));
+		break;
+	case PROP_MANAGER:
+		g_value_set_object (value, gck_user_storage_get_manager (self));
+		break;
+	case PROP_LOGIN:
+		g_value_set_object (value, gck_user_storage_get_login (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_user_storage_class_init (GckUserStorageClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckStoreClass *store_class = GCK_STORE_CLASS (klass);
+    
+	gobject_class->constructor = gck_user_storage_constructor;
+	gobject_class->dispose = gck_user_storage_dispose;
+	gobject_class->finalize = gck_user_storage_finalize;
+	gobject_class->set_property = gck_user_storage_set_property;
+	gobject_class->get_property = gck_user_storage_get_property;
+	
+	store_class->read_value = gck_user_storage_real_read_value;
+	store_class->write_value = gck_user_storage_real_write_value;
+    
+	g_object_class_install_property (gobject_class, PROP_DIRECTORY,
+	           g_param_spec_string ("directory", "Storage Directory", "Directory for storage", 
+	                                NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    
+	g_object_class_install_property (gobject_class, PROP_MANAGER,
+	           g_param_spec_object ("manager", "Object Manager", "Object Manager", 
+	                                GCK_TYPE_MANAGER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (gobject_class, PROP_LOGIN,
+	           g_param_spec_object ("login", "Login", "Login used to unlock", 
+	                                GCK_TYPE_LOGIN, G_PARAM_READABLE));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+
+GckUserStorage*
+gck_user_storage_new (GckManager *manager, const gchar *directory)
+{
+	g_return_val_if_fail (GCK_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (directory, NULL);
+	
+	return g_object_new (GCK_TYPE_USER_STORAGE, 
+	                     "manager", manager, 
+	                     "directory", directory, 
+	                     NULL);
+}
+
+CK_RV
+gck_user_storage_refresh (GckUserStorage *self)
+{
+	g_return_val_if_fail (GCK_USER_STORAGE (self), CKR_GENERAL_ERROR);
+	return refresh_with_login (self, self->login);
+}
+
+void
+gck_user_storage_create (GckUserStorage *self, GckTransaction *transaction, GckObject *object)
+{
+	gboolean is_private;
+	GckDataResult res;
+	gchar *identifier;
+	guchar *data;
+	gsize n_data;
+	gchar *path;
+	
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+	g_return_if_fail (GCK_IS_OBJECT (object));
+	
+	/* Make sure we haven't already stored it */
+	identifier = g_hash_table_lookup (self->object_to_identifier, object);
+	g_return_if_fail (identifier == NULL);
+	
+	/* Double check that the object is in fact serializable */
+	if (!GCK_IS_SERIALIZABLE (object)) {
+		g_warning ("can't store object of type '%s' on token", G_OBJECT_TYPE_NAME (object));
+		gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+		g_return_if_reached ();
+	}
+	
+	/* Figure out whether this is a private object */ 
+	if (!gck_object_get_attribute_boolean (object, CKA_PRIVATE, &is_private))
+		is_private = FALSE;
+	
+	/* Can't serialize private if we're not unlocked */
+	if (is_private && !self->login) {
+		gck_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
+		return;
+	}
+
+	/* Hook ourselves into the transaction */
+	if (!begin_write_state_and_refresh (self, transaction))
+		return;
+
+	/* Create an identifier guaranteed unique by this transaction */
+	identifier = identifier_for_object (object);
+	if (gck_data_file_unique_entry (self->file, &identifier) != GCK_DATA_SUCCESS) {
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		g_return_if_reached ();
+	}
+	
+	res = gck_data_file_create_entry (self->file, identifier, 
+	                                  is_private ? GCK_DATA_FILE_SECTION_PRIVATE : GCK_DATA_FILE_SECTION_PUBLIC);
+	switch(res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		g_free (identifier);
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		return;
+	case GCK_DATA_LOCKED:
+		g_free (identifier);
+		gck_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
+		return;
+	case GCK_DATA_SUCCESS:
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	/* Serialize the object in question */
+	res = gck_serializable_save (GCK_SERIALIZABLE (object), is_private ? self->login : NULL, &data, &n_data);
+	if (res != GCK_DATA_SUCCESS) {
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		g_return_if_reached ();
+	}
+
+	path = g_build_filename (self->directory, identifier, NULL);
+	gck_transaction_write_file (transaction, path, data, n_data);
+
+	/* Now we decide to own the object */
+	if (!gck_transaction_get_failed (transaction))
+		take_object_ownership (self, identifier, object);
+	
+	g_free (identifier);
+	g_free (path);
+	g_free (data);
+}
+
+void
+gck_user_storage_destroy (GckUserStorage *self, GckTransaction *transaction, GckObject *object)
+{
+	GckDataResult res;
+	gchar *identifier;
+	gchar *path;
+	
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+	g_return_if_fail (object);
+	
+	/* Lookup the object identifier */
+	identifier = g_hash_table_lookup (self->object_to_identifier, object);
+	g_return_if_fail (identifier);
+	
+	if (!begin_write_state_and_refresh (self, transaction))
+		return;
+
+	/* First actually delete the file */
+	path = g_build_filename (self->directory, identifier, NULL);
+	gck_transaction_remove_file (transaction, path);
+	g_free (path);
+	
+	if (gck_transaction_get_failed (transaction))
+		return;
+
+	/* Now delete the entry from our store */
+	res = gck_data_file_destroy_entry (self->file, identifier);
+	switch(res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		return;
+	case GCK_DATA_LOCKED:
+		gck_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
+		return;
+	case GCK_DATA_SUCCESS:
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	/* Actual removal of object happened as a callback above */
+	g_return_if_fail (g_hash_table_lookup (self->object_to_identifier, object) == NULL);
+}
+
+void
+gck_user_storage_relock (GckUserStorage *self, GckTransaction *transaction, 
+                         GckLogin *old_login, GckLogin *new_login)
+{
+	GckDataFile *file;
+	GckDataResult res;
+	RelockArgs args;
+	
+	g_return_if_fail (GCK_IS_USER_STORAGE (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	
+	/* Reload the file with the old password and start transaction */
+	if (!begin_write_state (self, transaction))
+		return;
+	
+	file = gck_data_file_new ();
+	
+	/* Read in from the old file */
+	res = gck_data_file_read_fd (file, self->read_fd, old_login);
+	switch(res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		return;
+	case GCK_DATA_LOCKED:
+		gck_transaction_fail (transaction, CKR_PIN_INCORRECT);
+		return;
+	case GCK_DATA_SUCCESS:
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	/* Write out to new path as new file */
+	res = gck_data_file_write_fd (file, self->write_fd, new_login);
+	switch(res) {
+	case GCK_DATA_FAILURE:
+	case GCK_DATA_UNRECOGNIZED:
+		gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		return;
+	case GCK_DATA_LOCKED:
+		gck_transaction_fail (transaction, CKR_PIN_INVALID);
+		return;
+	case GCK_DATA_SUCCESS:
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	/* Now go through all objects in the file, and load and reencode them */
+	args.transaction = transaction;
+	args.old_login = old_login;
+	args.new_login = new_login;
+	gck_data_file_foreach_entry (file, relock_each_object, &args);
+	
+	if (!gck_transaction_get_failed (transaction))
+		set_storage_login (self, new_login);
+	
+	g_object_unref (file);
+}
+
+CK_RV
+gck_user_storage_unlock (GckUserStorage *self, GckLogin *login)
+{
+	CK_RV rv;
+	
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (self), CKR_GENERAL_ERROR);
+	g_return_val_if_fail (!self->transaction, CKR_GENERAL_ERROR);
+	
+	if (self->login)
+		return CKR_USER_ALREADY_LOGGED_IN;
+	
+	rv = refresh_with_login (self, login);
+	if (rv == CKR_USER_NOT_LOGGED_IN)
+		return CKR_PIN_INCORRECT;
+	else if (rv == CKR_OK)
+		set_storage_login (self, login);
+	
+	return rv;
+}
+
+CK_RV
+gck_user_storage_lock (GckUserStorage *self)
+{
+	CK_RV rv;
+	
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (self), CKR_GENERAL_ERROR);
+	g_return_val_if_fail (!self->transaction, CKR_GENERAL_ERROR);
+	
+	if (!self->login)
+		return CKR_USER_NOT_LOGGED_IN;
+	
+	rv = refresh_with_login (self, NULL);
+	if (rv == CKR_OK)
+		set_storage_login (self, NULL);
+	
+	return rv;
+}
+
+GckManager*
+gck_user_storage_get_manager (GckUserStorage *self)
+{
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (self), NULL);
+	return self->manager;
+}
+
+const gchar*
+gck_user_storage_get_directory (GckUserStorage *self)
+{
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (self), NULL);
+	return self->directory;
+}
+
+GckLogin*
+gck_user_storage_get_login (GckUserStorage *self)
+{
+	g_return_val_if_fail (GCK_IS_USER_STORAGE (self), NULL);
+	return self->login;
+}

Added: trunk/pkcs11/user-store/gck-user-storage.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-storage.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,77 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_USER_STORAGE_H__
+#define __GCK_USER_STORAGE_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-store.h"
+#include "gck/gck-transaction.h"
+
+#define GCK_TYPE_USER_STORAGE               (gck_user_storage_get_type ())
+#define GCK_USER_STORAGE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_STORAGE, GckUserStorage))
+#define GCK_USER_STORAGE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_STORAGE, GckUserStorageClass))
+#define GCK_IS_USER_STORAGE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_STORAGE))
+#define GCK_IS_USER_STORAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_STORAGE))
+#define GCK_USER_STORAGE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_STORAGE, GckUserStorageClass))
+
+typedef struct _GckUserStorage GckUserStorage;
+typedef struct _GckUserStorageClass GckUserStorageClass;
+    
+struct _GckUserStorageClass {
+	GckStoreClass parent_class;
+};
+
+GType                       gck_user_storage_get_type               (void);
+
+GckUserStorage*             gck_user_storage_new                    (GckManager *manager,
+                                                                     const gchar *directory);
+
+GckManager*                 gck_user_storage_get_manager            (GckUserStorage *self);
+
+const gchar*                gck_user_storage_get_directory          (GckUserStorage *self);
+
+GckLogin*                   gck_user_storage_get_login              (GckUserStorage *self);
+
+CK_RV                       gck_user_storage_refresh                (GckUserStorage *self);
+
+void                        gck_user_storage_create                 (GckUserStorage *self, 
+                                                                     GckTransaction *transaction, 
+                                                                     GckObject *object);
+
+void                        gck_user_storage_destroy                (GckUserStorage *self, 
+                                                                     GckTransaction *transaction, 
+                                                                     GckObject *object);
+
+void                        gck_user_storage_relock                 (GckUserStorage *self, 
+                                                                     GckTransaction *transaction, 
+                                                                     GckLogin *old_login, 
+                                                                     GckLogin *new_login);
+
+CK_RV                       gck_user_storage_unlock                 (GckUserStorage *self,
+                                                                     GckLogin *login);
+
+CK_RV                       gck_user_storage_lock                   (GckUserStorage *self);
+
+#endif /* __GCK_USER_STORAGE_H__ */

Added: trunk/pkcs11/user-store/gck-user-store.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-store.h	Sat Jan 17 20:17:52 2009
@@ -0,0 +1,29 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2008 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef __GCK_USER_STORE_H__
+#define __GCK_USER_STORE_H__
+
+#include "pkcs11/pkcs11.h"
+
+CK_FUNCTION_LIST_PTR  gck_user_store_get_functions  (void);
+
+#endif /* __GCK_USER_STORE_H__ */

Modified: trunk/tests/gtest-helpers.c
==============================================================================
--- trunk/tests/gtest-helpers.c	(original)
+++ trunk/tests/gtest-helpers.c	Sat Jan 17 20:17:52 2009
@@ -94,6 +94,16 @@
 	return g_build_filename (test_path, basename, NULL);
 }
 
+guchar* 
+test_read_testdata (const gchar *basename, gsize *n_result)
+{
+	gchar *file = g_build_filename ("test-data", basename, NULL);
+	gchar *result;
+	if (!g_file_get_contents (file, &result, n_result, NULL))
+		g_assert_not_reached ();
+	return (guchar*)result;
+}
+
 static void 
 chdir_base_dir (char* argv0)
 {

Modified: trunk/tests/gtest-helpers.h
==============================================================================
--- trunk/tests/gtest-helpers.h	(original)
+++ trunk/tests/gtest-helpers.h	Sat Jan 17 20:17:52 2009
@@ -39,6 +39,7 @@
 void test_mainloop_run (int timeout);
 GMainLoop* test_mainloop_get (void);
 
+guchar* test_read_testdata (const gchar *basename, gsize *n_data);
 gchar* test_build_filename (const gchar *basename);
 
 #define DECLARE_SETUP(x) \



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