[gcr] gcr: Implement functions for loading and using SubjectPublicKeyInfo



commit c2c73dfc3ff6c94aa5615c1c8047ed8b862f7b6f
Author: Stef Walter <stefw collabora co uk>
Date:   Fri Nov 18 13:22:40 2011 +0100

    gcr: Implement functions for loading and using SubjectPublicKeyInfo
    
     * Move key calculation routines into gcr-subject-public-key.c
     * Move creation of SPK from gcr-fingerprint.c into here
     * Implement loading of SPK from PKCS#11 certificates, public keys,
       private keys
     * Add support for parsing SubjectPublicKeyInfo to parser. Generates
       attributes for a CKO_PUBLIC_KEY
     * Fix bugs in DER encoding related to this.
     * More tweaks on testing infrastructure

 docs/reference/gcr/gcr-sections.txt |    1 -
 egg/egg-asn1x.c                     |   11 +-
 egg/egg-testing.h                   |    2 +
 gck/gck-mock.c                      |    7 +-
 gcr/Makefile.am                     |    4 +-
 gcr/gcr-base.symbols                |    1 -
 gcr/gcr-certificate-req-renderer.c  |   16 +-
 gcr/gcr-certificate.c               |    4 +-
 gcr/gcr-debug.c                     |    1 +
 gcr/gcr-debug.h                     |    1 +
 gcr/gcr-fingerprint.c               |  327 +-------------
 gcr/gcr-fingerprint.h               |    4 -
 gcr/gcr-key-size.c                  |  106 -----
 gcr/gcr-key-size.h                  |   33 --
 gcr/gcr-parser.c                    |  117 +++++
 gcr/gcr-subject-public-key.c        |  848 +++++++++++++++++++++++++++++++++++
 gcr/gcr-subject-public-key.h        |   55 +++
 gcr/gcr-types.h                     |    2 +
 gcr/tests/Makefile.am               |    1 +
 gcr/tests/files/client.spk          |  Bin 0 -> 294 bytes
 gcr/tests/files/generic-dsa.spk     |  Bin 0 -> 443 bytes
 gcr/tests/test-subject-public-key.c |  721 +++++++++++++++++++++++++++++
 po/POTFILES.in                      |    1 +
 23 files changed, 1785 insertions(+), 478 deletions(-)
---
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 9e52421..adcb740 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -616,7 +616,6 @@ GcrSecretExchangePrivate
 <SECTION>
 <FILE>gcr-fingerprint</FILE>
 gcr_fingerprint_from_attributes
-gcr_fingerprint_from_certificate_public_key
 gcr_fingerprint_from_subject_public_key_info
 </SECTION>
 
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
index b7890e8..fc80d8f 100644
--- a/egg/egg-asn1x.c
+++ b/egg/egg-asn1x.c
@@ -1703,6 +1703,8 @@ anode_encoder_bit_string (gpointer user_data,
 static gboolean
 anode_encode_prepare_simple (GNode *node, gboolean want)
 {
+	EggBytes *backing;
+	EggBytes *bytes;
 	Aenc *enc;
 	Atlv *tlv;
 
@@ -1713,8 +1715,13 @@ anode_encode_prepare_simple (GNode *node, gboolean want)
 	/* Transfer the tlv data over to enc */
 	enc = anode_get_enc_data (node);
 	if (enc == NULL) {
-		anode_set_enc_data (node, anode_encoder_data,
-		                    (guchar *)tlv->buf + tlv->off, NULL);
+		backing = anode_get_backing (node);
+		if (backing == NULL)
+			return FALSE;
+
+		bytes = egg_bytes_new_with_free_func ((guchar *)tlv->buf + tlv->off, tlv->len,
+		                                      egg_bytes_unref, egg_bytes_ref (backing));
+		anode_set_enc_data (node, anode_encoder_bytes, bytes, egg_bytes_unref);
 	}
 
 	tlv->buf = tlv->end = NULL;
diff --git a/egg/egg-testing.h b/egg/egg-testing.h
index a08c34a..a15925a 100644
--- a/egg/egg-testing.h
+++ b/egg/egg-testing.h
@@ -67,6 +67,8 @@ gchar *    egg_test_escape_data                (const guchar *data,
 
 void       egg_test_wait_stop                  (void);
 
+#define    egg_test_wait()                     g_assert (egg_test_wait_until (20000) != FALSE)
+
 gboolean   egg_test_wait_until                 (int timeout);
 
 gint       egg_tests_run_with_loop             (void);
diff --git a/gck/gck-mock.c b/gck/gck-mock.c
index a1fdba9..716b149 100644
--- a/gck/gck-mock.c
+++ b/gck/gck-mock.c
@@ -913,13 +913,12 @@ gck_mock_C_GetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObje
 	CK_ULONG i;
 
 	session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
-	g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
 
 	attrs = lookup_object (session, hObject);
-	if (!attrs) {
-		g_assert_not_reached (); /* "invalid object handle passed" */
+	if (!attrs)
 		return CKR_OBJECT_HANDLE_INVALID;
-	}
 
 	for (i = 0; i < ulCount; ++i) {
 		result = pTemplate + i;
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 2045125..addfca0 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -113,7 +113,6 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-importer.c gcr-importer.h \
 	gcr-import-interaction.c gcr-import-interaction.h \
 	gcr-internal.h \
-	gcr-key-size.c gcr-key-size.h \
 	gcr-library.c gcr-library.h \
 	gcr-memory.c \
 	gcr-memory-icon.c gcr-memory-icon.h \
@@ -127,6 +126,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-simple-certificate.c gcr-simple-certificate.h \
 	gcr-simple-collection.c gcr-simple-collection.h \
 	gcr-single-collection.c gcr-single-collection.h \
+	gcr-subject-public-key.c gcr-subject-public-key.h \
 	gcr-trust.c gcr-trust.h \
 	gcr-types.h \
 	gcr-union-collection.c gcr-union-collection.h \
@@ -152,7 +152,6 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-gnupg-records.c gcr-gnupg-records.h \
 	gcr-import-button.c gcr-import-button.h \
 	gcr-key-renderer.c gcr-key-renderer.h \
-	gcr-key-size.c gcr-key-size.h \
 	gcr-key-widget.c gcr-key-widget.h \
 	gcr-list-selector.c gcr-list-selector.h gcr-list-selector-private.h \
 	gcr-live-search.c gcr-live-search.h \
@@ -163,6 +162,7 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-pkcs11-import-dialog.c gcr-pkcs11-import-dialog.h \
 	gcr-record.c gcr-record.h \
 	gcr-renderer.c gcr-renderer.h \
+	gcr-subject-public-key.c gcr-subject-public-key.h \
 	gcr-tree-selector.c gcr-tree-selector.h \
 	gcr-unlock-options.h \
 	gcr-unlock-options-widget.c gcr-unlock-options-widget.h \
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index 7d0be13..8d96291 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -59,7 +59,6 @@ gcr_filter_collection_new_with_callback
 gcr_filter_collection_refilter
 gcr_filter_collection_set_callback
 gcr_fingerprint_from_attributes
-gcr_fingerprint_from_certificate_public_key
 gcr_fingerprint_from_subject_public_key_info
 gcr_icon_for_token
 gcr_importer_create_for_parsed
diff --git a/gcr/gcr-certificate-req-renderer.c b/gcr/gcr-certificate-req-renderer.c
index 4b41bee..a6b4f01 100644
--- a/gcr/gcr-certificate-req-renderer.c
+++ b/gcr/gcr-certificate-req-renderer.c
@@ -22,20 +22,8 @@
 #include "gcr-certificate-renderer-private.h"
 #include "gcr-certificate-req-renderer.h"
 #include "gcr-display-view.h"
-#include "gcr-key-size.h"
 #include "gcr-oids.h"
-
-#if 0
-#include "gcr-certificate.h"
-#include "gcr-certificate-exporter.h"
-#include "gcr-certificate-extensions.h"
-#include "gcr-certificate-renderer.h"
-#include "gcr-fingerprint.h"
-#include "gcr-icons.h"
-#include "gcr-oids.h"
-#include "gcr-simple-certificate.h"
-#include "gcr-renderer.h"
-#endif
+#include "gcr-subject-public-key.h"
 
 #include "egg/egg-asn1x.h"
 #include "egg/egg-asn1-defs.h"
@@ -299,7 +287,7 @@ ensure_key_size (GcrCertificateReqRenderer *self,
 	if (self->pv->key_size)
 		return self->pv->key_size;
 
-	self->pv->key_size = _gcr_key_size_calculate (public_key);
+	self->pv->key_size = _gcr_subject_public_key_calculate_size (public_key);
 	return self->pv->key_size;
 }
 
diff --git a/gcr/gcr-certificate.c b/gcr/gcr-certificate.c
index 1713692..d0ab5a6 100644
--- a/gcr/gcr-certificate.c
+++ b/gcr/gcr-certificate.c
@@ -25,8 +25,8 @@
 #include "gcr-comparable.h"
 #include "gcr-icons.h"
 #include "gcr-internal.h"
-#include "gcr-key-size.h"
 #include "gcr-oids.h"
+#include "gcr-subject-public-key.h"
 
 #include "egg/egg-asn1x.h"
 #include "egg/egg-asn1-defs.h"
@@ -735,7 +735,7 @@ gcr_certificate_get_key_size (GcrCertificate *self)
 	if (!info->key_size) {
 		subject_public_key = egg_asn1x_node (info->asn1, "tbsCertificate",
 		                                     "subjectPublicKeyInfo", NULL);
-		info->key_size = _gcr_key_size_calculate (subject_public_key);
+		info->key_size = _gcr_subject_public_key_calculate_size (subject_public_key);
 	}
 
 	return info->key_size;
diff --git a/gcr/gcr-debug.c b/gcr/gcr-debug.c
index fea8fee..9177e58 100644
--- a/gcr/gcr-debug.c
+++ b/gcr/gcr-debug.c
@@ -42,6 +42,7 @@ static GDebugKey keys[] = {
 	{ "gnupg", GCR_DEBUG_GNUPG },
 	{ "trust", GCR_DEBUG_TRUST },
 	{ "import", GCR_DEBUG_IMPORT },
+	{ "key", GCR_DEBUG_KEY },
 	{ 0, }
 };
 
diff --git a/gcr/gcr-debug.h b/gcr/gcr-debug.h
index 20bc35f..5063b77 100644
--- a/gcr/gcr-debug.h
+++ b/gcr/gcr-debug.h
@@ -34,6 +34,7 @@ typedef enum {
 	GCR_DEBUG_GNUPG = 1 << 4,
 	GCR_DEBUG_TRUST = 1 << 5,
 	GCR_DEBUG_IMPORT = 1 << 6,
+	GCR_DEBUG_KEY = 1 << 7,
 } GcrDebugFlags;
 
 gboolean           _gcr_debug_flag_is_set              (GcrDebugFlags flag);
diff --git a/gcr/gcr-fingerprint.c b/gcr/gcr-fingerprint.c
index 1011797..82b24c6 100644
--- a/gcr/gcr-fingerprint.c
+++ b/gcr/gcr-fingerprint.c
@@ -25,12 +25,12 @@
 
 #include "gcr-fingerprint.h"
 #include "gcr-oids.h"
+#include "gcr-subject-public-key.h"
 
 #include "egg/egg-asn1x.h"
 #include "egg/egg-asn1-defs.h"
 
 #include <glib.h>
-#include <gcrypt.h>
 
 /**
  * SECTION:gcr-fingerprint
@@ -86,262 +86,6 @@ gcr_fingerprint_from_subject_public_key_info (const guchar *key_info,
 	return fingerprint;
 }
 
-static gboolean
-rsa_subject_public_key_from_attributes (GckAttributes *attrs, GNode *info_asn)
-{
-	GckAttribute *modulus;
-	GckAttribute *exponent;
-	EggBytes *key;
-	EggBytes *params;
-	GNode *key_asn;
-	GNode *params_asn;
-	EggBytes *usg;
-
-	_gcr_oids_init ();
-
-	modulus = gck_attributes_find (attrs, CKA_MODULUS);
-	exponent = gck_attributes_find (attrs, CKA_PUBLIC_EXPONENT);
-	if (modulus == NULL || exponent == NULL)
-		return FALSE;
-
-	key_asn = egg_asn1x_create (pk_asn1_tab, "RSAPublicKey");
-	g_return_val_if_fail (key_asn, FALSE);
-
-	params_asn = egg_asn1x_create (pk_asn1_tab, "RSAParameters");
-	g_return_val_if_fail (params_asn, FALSE);
-
-	usg = egg_bytes_new_with_free_func (modulus->value, modulus->length,
-	                                    gck_attributes_unref,
-	                                    gck_attributes_ref (attrs));
-	egg_asn1x_set_integer_as_usg (egg_asn1x_node (key_asn, "modulus", NULL), usg);
-	egg_bytes_unref (usg);
-
-	usg = egg_bytes_new_with_free_func (exponent->value, exponent->length,
-	                                    gck_attributes_unref,
-	                                    gck_attributes_ref (attrs));
-	egg_asn1x_set_integer_as_usg (egg_asn1x_node (key_asn, "publicExponent", NULL), usg);
-	egg_bytes_unref (usg);
-
-	key = egg_asn1x_encode (key_asn, NULL);
-	egg_asn1x_destroy (key_asn);
-
-	egg_asn1x_set_null (params_asn);
-
-	params = egg_asn1x_encode (params_asn, g_realloc);
-	egg_asn1x_destroy (params_asn);
-
-	egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
-	                           key, egg_bytes_get_size (key) * 8);
-
-	egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), GCR_OID_PKIX1_RSA);
-	egg_asn1x_set_element_raw (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params);
-
-	egg_bytes_unref (key);
-	egg_bytes_unref (params);
-	return TRUE;
-}
-
-static gboolean
-dsa_subject_public_key_from_private (GNode *key_asn, GckAttribute *ap,
-                                     GckAttribute *aq, GckAttribute *ag, GckAttribute *ax)
-{
-	gcry_mpi_t mp, mq, mg, mx, my;
-	size_t n_buffer;
-	gcry_error_t gcry;
-	unsigned char *buffer;
-
-	gcry = gcry_mpi_scan (&mp, GCRYMPI_FMT_USG, ap->value, ap->length, NULL);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	gcry = gcry_mpi_scan (&mq, GCRYMPI_FMT_USG, aq->value, aq->length, NULL);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	gcry = gcry_mpi_scan (&mg, GCRYMPI_FMT_USG, ag->value, ag->length, NULL);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	gcry = gcry_mpi_scan (&mx, GCRYMPI_FMT_USG, ax->value, ax->length, NULL);
-	g_return_val_if_fail (gcry == 0, FALSE);
-
-	/* Calculate the public part from the private */
-	my = gcry_mpi_snew (gcry_mpi_get_nbits (mx));
-	g_return_val_if_fail (my, FALSE);
-	gcry_mpi_powm (my, mg, mx, mp);
-
-	gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, my);
-	g_return_val_if_fail (gcry == 0, FALSE);
-	egg_asn1x_take_integer_as_raw (key_asn, egg_bytes_new_with_free_func (buffer, n_buffer,
-	                                                                      gcry_free, buffer));
-
-	gcry_mpi_release (mp);
-	gcry_mpi_release (mq);
-	gcry_mpi_release (mg);
-	gcry_mpi_release (mx);
-	gcry_mpi_release (my);
-
-	return TRUE;
-}
-
-static gboolean
-dsa_subject_public_key_from_attributes (GckAttributes *attrs,
-                                        gulong klass,
-                                        GNode *info_asn)
-{
-	GckAttribute *value, *g, *q, *p;
-	GNode *key_asn, *params_asn;
-	EggBytes *key;
-	EggBytes *params;
-
-	_gcr_oids_init ();
-
-	p = gck_attributes_find (attrs, CKA_PRIME);
-	q = gck_attributes_find (attrs, CKA_SUBPRIME);
-	g = gck_attributes_find (attrs, CKA_BASE);
-	value = gck_attributes_find (attrs, CKA_VALUE);
-
-	if (p == NULL || q == NULL || g == NULL || value == NULL)
-		return FALSE;
-
-	key_asn = egg_asn1x_create (pk_asn1_tab, "DSAPublicPart");
-	g_return_val_if_fail (key_asn, FALSE);
-
-	params_asn = egg_asn1x_create (pk_asn1_tab, "DSAParameters");
-	g_return_val_if_fail (params_asn, FALSE);
-
-	egg_asn1x_take_integer_as_usg (egg_asn1x_node (params_asn, "p", NULL),
-	                               egg_bytes_new_with_free_func (p->value, p->length,
-	                                                             gck_attributes_unref,
-	                                                             gck_attributes_ref (attrs)));
-	egg_asn1x_take_integer_as_usg (egg_asn1x_node (params_asn, "q", NULL),
-	                               egg_bytes_new_with_free_func (q->value, q->length,
-	                                                             gck_attributes_unref,
-	                                                             gck_attributes_ref (attrs)));
-	egg_asn1x_take_integer_as_usg (egg_asn1x_node (params_asn, "g", NULL),
-	                               egg_bytes_new_with_free_func (g->value, g->length,
-	                                                             gck_attributes_unref,
-	                                                             gck_attributes_ref (attrs)));
-
-	/* Are these attributes for a public or private key? */
-	if (klass == CKO_PRIVATE_KEY) {
-
-		/* We need to calculate the public from the private key */
-		if (!dsa_subject_public_key_from_private (key_asn, p, q, g, value))
-			g_return_val_if_reached (FALSE);
-
-	} else if (klass == CKO_PUBLIC_KEY) {
-		egg_asn1x_take_integer_as_usg (key_asn,
-		                               egg_bytes_new_with_free_func (value->value, value->length,
-		                                                             gck_attributes_unref,
-		                                                             gck_attributes_ref (attrs)));
-
-	} else {
-		g_assert_not_reached ();
-	}
-
-	key = egg_asn1x_encode (key_asn, NULL);
-	egg_asn1x_destroy (key_asn);
-
-	params = egg_asn1x_encode (params_asn, NULL);
-	egg_asn1x_destroy (params_asn);
-
-	egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
-	                            key, egg_bytes_get_size (key) * 8);
-	egg_asn1x_set_element_raw (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params);
-
-	egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), GCR_OID_PKIX1_DSA);
-
-	egg_bytes_unref (key);
-	egg_bytes_unref (params);
-	return TRUE;
-}
-
-static gpointer
-fingerprint_from_key_attributes (GckAttributes *attrs,
-                                 gulong klass,
-                                 GChecksumType checksum_type,
-                                 gsize *n_fingerprint)
-{
-	gpointer fingerprint = NULL;
-	gboolean ret = FALSE;
-	GNode *info_asn;
-	EggBytes *info;
-	gulong key_type;
-
-	if (!gck_attributes_find_ulong (attrs, CKA_KEY_TYPE, &key_type))
-		return NULL;
-
-	info_asn = egg_asn1x_create (pkix_asn1_tab, "SubjectPublicKeyInfo");
-	g_return_val_if_fail (info_asn, NULL);
-
-	if (key_type == CKK_RSA)
-		ret = rsa_subject_public_key_from_attributes (attrs, info_asn);
-
-	else if (key_type == CKK_DSA)
-		ret = dsa_subject_public_key_from_attributes (attrs, klass, info_asn);
-
-	else
-		ret = FALSE;
-
-	if (ret) {
-		info = egg_asn1x_encode (info_asn, NULL);
-		fingerprint = gcr_fingerprint_from_subject_public_key_info (egg_bytes_get_data (info),
-		                                                            egg_bytes_get_size (info),
-		                                                            checksum_type,
-		                                                            n_fingerprint);
-		egg_bytes_unref (info);
-	}
-
-	egg_asn1x_destroy (info_asn);
-	return fingerprint;
-}
-
-static guchar *
-fingerprint_from_cert_value (EggBytes *der_data,
-                             GChecksumType checksum_type,
-                             gsize *n_fingerprint)
-{
-	guchar *fingerprint;
-	GNode *cert_asn;
-	EggBytes *info;
-
-	cert_asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", der_data);
-	if (cert_asn == NULL)
-		return NULL;
-
-	info = egg_asn1x_get_element_raw (egg_asn1x_node (cert_asn, "tbsCertificate", "subjectPublicKeyInfo", NULL));
-	g_return_val_if_fail (info != NULL, NULL);
-
-	fingerprint = gcr_fingerprint_from_subject_public_key_info (egg_bytes_get_data (info),
-	                                                            egg_bytes_get_size (info),
-	                                                            checksum_type,
-	                                                            n_fingerprint);
-
-	egg_bytes_unref (info);
-	egg_asn1x_destroy (cert_asn);
-	return fingerprint;
-}
-
-static guchar *
-fingerprint_from_cert_attributes (GckAttributes *attrs,
-                                  GChecksumType checksum_type,
-                                  gsize *n_fingerprint)
-{
-	GckAttribute *attr;
-	EggBytes *bytes;
-	guchar *fingerprint;
-
-	attr = gck_attributes_find (attrs, CKA_VALUE);
-	if (attr == NULL)
-		return NULL;
-
-	bytes = egg_bytes_new_with_free_func (attr->value, attr->length,
-	                                      gck_attributes_unref,
-	                                      gck_attributes_ref (attrs));
-	fingerprint = fingerprint_from_cert_value (bytes, checksum_type, n_fingerprint);
-
-	egg_bytes_unref (bytes);
-	return fingerprint;
-}
-
 /**
  * gcr_fingerprint_from_attributes:
  * @attrs: attributes for key or certificate
@@ -357,62 +101,27 @@ fingerprint_from_cert_attributes (GckAttributes *attrs,
  */
 guchar *
 gcr_fingerprint_from_attributes (GckAttributes *attrs,
-                                  GChecksumType checksum_type,
-                                  gsize *n_fingerprint)
-{
-	gulong klass;
-
-	g_return_val_if_fail (attrs, FALSE);
-	g_return_val_if_fail (n_fingerprint, FALSE);
-
-	if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
-		return NULL;
-
-	if (klass == CKO_CERTIFICATE)
-		return fingerprint_from_cert_attributes (attrs, checksum_type,
-		                                         n_fingerprint);
-
-	else if (klass == CKO_PUBLIC_KEY || klass == CKO_PRIVATE_KEY)
-		return fingerprint_from_key_attributes (attrs, klass,
-		                                        checksum_type,
-		                                        n_fingerprint);
-
-	else
-		return NULL;
-}
-
-/**
- * gcr_fingerprint_from_certificate_public_key:
- * @certificate: the certificate
- * @checksum_type: the type of fingerprint to create
- * @n_fingerprint: the length of fingerprint returned
- *
- * Create a key fingerprint for a certificate's public key. Note that this is
- * not a fingerprint of certificate data, which you would use
- * gcr_certificate_get_fingerprint() for.
- *
- * Returns: (transfer full) (allow-none) (array length=n_fingerprint): the
- *          fingerprint or %NULL if the input was invalid.
- */
-guchar *
-gcr_fingerprint_from_certificate_public_key (GcrCertificate *certificate,
-                                             GChecksumType checksum_type,
-                                             gsize *n_fingerprint)
+                                 GChecksumType checksum_type,
+                                 gsize *n_fingerprint)
 {
-	const guchar *der_data;
-	gsize n_der_data;
-	EggBytes *bytes;
-	guchar *fingerprint;
+	gpointer fingerprint = NULL;
+	EggBytes *info;
+	GNode *asn;
 
-	g_return_val_if_fail (GCR_IS_CERTIFICATE (certificate), NULL);
+	g_return_val_if_fail (attrs != NULL, NULL);
+	g_return_val_if_fail (n_fingerprint, NULL);
 
-	der_data = gcr_certificate_get_der_data (certificate, &n_der_data);
-	g_return_val_if_fail (der_data != NULL, NULL);
+	asn = _gcr_subject_public_key_for_attributes (attrs);
 
-	bytes = egg_bytes_new_with_free_func (der_data, n_der_data, g_object_unref,
-	                                      g_object_ref (certificate));
-	fingerprint = fingerprint_from_cert_value (bytes, checksum_type, n_fingerprint);
-	egg_bytes_unref (bytes);
+	if (asn != NULL) {
+		info = egg_asn1x_encode (asn, NULL);
+		fingerprint = gcr_fingerprint_from_subject_public_key_info (egg_bytes_get_data (info),
+		                                                            egg_bytes_get_size (info),
+		                                                            checksum_type,
+		                                                            n_fingerprint);
+		egg_bytes_unref (info);
+	}
 
+	egg_asn1x_destroy (asn);
 	return fingerprint;
 }
diff --git a/gcr/gcr-fingerprint.h b/gcr/gcr-fingerprint.h
index 84cc41f..b48f170 100644
--- a/gcr/gcr-fingerprint.h
+++ b/gcr/gcr-fingerprint.h
@@ -42,8 +42,4 @@ guchar *        gcr_fingerprint_from_attributes                 (GckAttributes *
                                                                  GChecksumType checksum_type,
                                                                  gsize *n_fingerprint);
 
-guchar *        gcr_fingerprint_from_certificate_public_key     (GcrCertificate *certificate,
-                                                                 GChecksumType checksum_type,
-                                                                 gsize *n_fingerprint);
-
 #endif /* GCR_FINGERPRINT_H_ */
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index e8a5116..7b4ea88 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -622,6 +622,120 @@ parse_der_private_key (GcrParser *self,
 }
 
 /* -----------------------------------------------------------------------------
+ * SUBJECT PUBLIC KEY
+ */
+
+static gint
+handle_subject_public_key_rsa (GcrParser *self,
+                               GcrParsed *parsed,
+                               EggBytes *key,
+                               EggBytes *params)
+{
+	gint res = GCR_ERROR_FAILURE;
+	GNode *asn = NULL;
+
+	asn = egg_asn1x_create_and_decode (pk_asn1_tab, "RSAPublicKey", key);
+	if (!asn)
+		goto done;
+
+	parsing_object (parsed, CKO_PUBLIC_KEY);
+	parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_RSA);
+
+	if (!parsed_asn1_number (parsed, asn, "modulus", CKA_MODULUS) ||
+	    !parsed_asn1_number (parsed, asn, "publicExponent", CKA_PUBLIC_EXPONENT))
+		goto done;
+
+	res = SUCCESS;
+
+done:
+	egg_asn1x_destroy (asn);
+	return res;
+}
+
+static gint
+handle_subject_public_key_dsa (GcrParser *self,
+                               GcrParsed *parsed,
+                               EggBytes *key,
+                               EggBytes *params)
+{
+	gint res = GCR_ERROR_FAILURE;
+	GNode *key_asn = NULL;
+	GNode *param_asn = NULL;
+
+	key_asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPublicPart", key);
+	param_asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", params);
+
+	if (!key_asn || !param_asn)
+		goto done;
+
+	parsing_object (parsed, CKO_PUBLIC_KEY);
+	parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_DSA);
+
+	if (!parsed_asn1_number (parsed, param_asn, "p", CKA_PRIME) ||
+	    !parsed_asn1_number (parsed, param_asn, "q", CKA_SUBPRIME) ||
+	    !parsed_asn1_number (parsed, param_asn, "g", CKA_BASE) ||
+	    !parsed_asn1_number (parsed, key_asn, NULL, CKA_VALUE))
+		goto done;
+
+	res = SUCCESS;
+
+done:
+	egg_asn1x_destroy (key_asn);
+	egg_asn1x_destroy (param_asn);
+	return res;
+}
+
+static gint
+parse_der_subject_public_key (GcrParser *self,
+                              EggBytes *data)
+{
+	GcrParsed *parsed;
+	EggBytes *params;
+	EggBytes *key;
+	GNode *asn = NULL;
+	GNode *node;
+	GQuark oid;
+	guint bits;
+	gint ret;
+
+	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SubjectPublicKeyInfo", data);
+	if (asn == NULL)
+		return GCR_ERROR_UNRECOGNIZED;
+
+	parsed = push_parsed (self, TRUE);
+	parsing_block (parsed, GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY, data);
+
+	node = egg_asn1x_node (asn, "algorithm", "algorithm", NULL);
+	oid = egg_asn1x_get_oid_as_quark (node);
+
+	node = egg_asn1x_node (asn, "algorithm", "parameters", NULL);
+	params = egg_asn1x_get_element_raw (node);
+
+	node = egg_asn1x_node (asn, "subjectPublicKey", NULL);
+	key = egg_asn1x_get_bits_as_raw (node, &bits);
+
+	if (oid == GCR_OID_PKIX1_RSA)
+		ret = handle_subject_public_key_rsa (self, parsed, key, params);
+
+	else if (oid == GCR_OID_PKIX1_DSA)
+		ret = handle_subject_public_key_dsa (self, parsed, key, params);
+
+	else
+		ret = GCR_ERROR_UNRECOGNIZED;
+
+	egg_bytes_unref (key);
+	egg_bytes_unref (params);
+
+	if (ret == SUCCESS)
+		parsed_fire (self, parsed);
+
+	pop_parsed (self, parsed);
+
+	egg_asn1x_destroy (asn);
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
  * PKCS8
  */
 
@@ -1968,6 +2082,7 @@ parse_openssh_public (GcrParser *self,
  * @GCR_FORMAT_DER_PRIVATE_KEY: DER encoded private key
  * @GCR_FORMAT_DER_PRIVATE_KEY_RSA: DER encoded RSA private key
  * @GCR_FORMAT_DER_PRIVATE_KEY_DSA: DER encoded DSA private key
+ * @GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY: DER encoded SubjectPublicKeyInfo
  * @GCR_FORMAT_DER_CERTIFICATE_X509: DER encoded X.509 certificate
  * @GCR_FORMAT_DER_PKCS7: DER encoded PKCS\#7 container file which can contain certificates
  * @GCR_FORMAT_DER_PKCS8: DER encoded PKCS\#8 file which can contain a key
@@ -2004,6 +2119,7 @@ static const ParserFormat parser_normal[] = {
 	{ GCR_FORMAT_BASE64_SPKAC, parse_base64_spkac },
 	{ GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
 	{ GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
+	{ GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY, parse_der_subject_public_key },
 	{ GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
 	{ GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
 	{ GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
@@ -2021,6 +2137,7 @@ static const ParserFormat parser_formats[] = {
 	{ GCR_FORMAT_DER_PRIVATE_KEY, parse_der_private_key },
 	{ GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
 	{ GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
+	{ GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY, parse_der_subject_public_key },
 	{ GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
 	{ GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
 	{ GCR_FORMAT_DER_PKCS8, parse_der_pkcs8 },
diff --git a/gcr/gcr-subject-public-key.c b/gcr/gcr-subject-public-key.c
new file mode 100644
index 0000000..fb93b56
--- /dev/null
+++ b/gcr/gcr-subject-public-key.c
@@ -0,0 +1,848 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+#include "config.h"
+
+#define DEBUG_FLAG GCR_DEBUG_KEY
+#include "gcr-debug.h"
+#include "gcr-oids.h"
+#include "gcr-subject-public-key.h"
+#include "gcr-types.h"
+
+#include "egg/egg-asn1x.h"
+#include "egg/egg-asn1-defs.h"
+#include "egg/egg-error.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <gcrypt.h>
+
+static gboolean
+check_object_basics (GckAttributes *attributes,
+                     gulong *klass,
+                     gulong *type)
+{
+	g_assert (klass != NULL);
+	g_assert (type != NULL);
+
+	if (!gck_attributes_find_ulong (attributes, CKA_CLASS, klass))
+		return FALSE;
+
+	if (*klass == CKO_PUBLIC_KEY || *klass == CKO_PRIVATE_KEY)
+		return gck_attributes_find_ulong (attributes, CKA_KEY_TYPE, type);
+
+	else if (*klass == CKO_CERTIFICATE)
+		return gck_attributes_find_ulong (attributes, CKA_CERTIFICATE_TYPE, type);
+
+	*type = GCK_INVALID;
+	return FALSE;
+}
+
+static gboolean
+load_object_basics (GckObject *object,
+                    GckAttributes *attributes,
+                    GCancellable *cancellable,
+                    gulong *klass,
+                    gulong *type,
+                    GError **lerror)
+{
+	GckAttributes *attrs;
+	GError *error = NULL;
+
+	g_assert (klass != NULL);
+	g_assert (type != NULL);
+
+	if (check_object_basics (attributes, klass, type)) {
+		_gcr_debug ("already loaded: class = %lu, type = %lu", *klass, *type);
+		return TRUE;
+	}
+
+	attrs = gck_object_get (object, cancellable, &error,
+	                        CKA_CLASS, CKA_KEY_TYPE, CKA_CERTIFICATE_TYPE, GCK_INVALID);
+	if (error != NULL) {
+		_gcr_debug ("couldn't load: %s", error->message);
+		g_propagate_error (lerror, error);
+		return FALSE;
+	}
+
+	gck_attributes_set_all (attributes, attrs);
+	gck_attributes_unref (attrs);
+
+	if (!check_object_basics (attributes, klass, type))
+		return FALSE;
+
+	_gcr_debug ("loaded: class = %lu, type = %lu", *klass, *type);
+	return TRUE;
+}
+
+static gboolean
+check_x509_attributes (GckAttributes *attributes)
+{
+	GckAttribute *value = gck_attributes_find (attributes, CKA_VALUE);
+	return (value && !gck_attribute_is_invalid (value));
+}
+
+static gboolean
+load_x509_attributes (GckObject *object,
+                      GckAttributes *attributes,
+                      GCancellable *cancellable,
+                      GError **lerror)
+{
+	GckAttributes *attrs;
+	GError *error = NULL;
+
+	if (check_x509_attributes (attributes)) {
+		_gcr_debug ("already loaded");
+		return TRUE;
+	}
+
+	attrs = gck_object_get (object, cancellable, &error,
+	                        CKA_VALUE, GCK_INVALID);
+	if (error != NULL) {
+		_gcr_debug ("couldn't load: %s", error->message);
+		g_propagate_error (lerror, error);
+		return FALSE;
+	}
+
+	gck_attributes_set_all (attributes, attrs);
+	gck_attributes_unref (attrs);
+
+	return check_x509_attributes (attributes);
+}
+
+static gboolean
+check_rsa_attributes (GckAttributes *attributes)
+{
+	GckAttribute *modulus;
+	GckAttribute *exponent;
+
+	modulus = gck_attributes_find (attributes, CKA_MODULUS);
+	exponent = gck_attributes_find (attributes, CKA_PUBLIC_EXPONENT);
+
+	return (modulus && !gck_attribute_is_invalid (modulus) &&
+	        exponent && !gck_attribute_is_invalid (exponent));
+}
+
+static gboolean
+load_rsa_attributes (GckObject *object,
+                     GckAttributes *attributes,
+                     GCancellable *cancellable,
+                     GError **lerror)
+{
+	GckAttributes *attrs;
+	GError *error = NULL;
+
+	if (check_rsa_attributes (attributes)) {
+		_gcr_debug ("rsa attributes already loaded");
+		return TRUE;
+	}
+
+	attrs = gck_object_get (object, cancellable, &error,
+	                        CKA_MODULUS, CKA_PUBLIC_EXPONENT, GCK_INVALID);
+	if (error != NULL) {
+		_gcr_debug ("couldn't load rsa attributes: %s", error->message);
+		g_propagate_error (lerror, error);
+		return FALSE;
+	}
+
+	gck_attributes_set_all (attributes, attrs);
+	gck_attributes_unref (attrs);
+
+	return check_rsa_attributes (attributes);
+}
+
+static GckObject *
+lookup_public_key (GckObject *object,
+                   GCancellable *cancellable,
+                   GError **lerror)
+{
+	GckAttributes *match;
+	GError *error = NULL;
+	GckSession *session;
+	GckObject *result;
+	GList *objects;
+	guchar *id;
+	gsize n_id;
+
+	id = gck_object_get_data (object, CKA_ID, cancellable, &n_id, &error);
+	if (error != NULL) {
+		_gcr_debug ("couldn't load private key id: %s", error->message);
+		g_propagate_error (lerror, error);
+		return NULL;
+	}
+
+	match = gck_attributes_new ();
+	gck_attributes_add_ulong (match, CKA_CLASS, CKO_PUBLIC_KEY);
+	gck_attributes_add_data (match, CKA_ID, id, n_id);
+	session = gck_object_get_session (object);
+	g_free (id);
+
+	objects = gck_session_find_objects (session, match, cancellable, &error);
+
+	gck_attributes_unref (match);
+	g_object_unref (session);
+
+	if (error != NULL) {
+		_gcr_debug ("couldn't lookup public key: %s", error->message);
+		g_propagate_error (lerror, error);
+		return NULL;
+	}
+
+	if (!objects)
+		return NULL;
+
+	result = g_object_ref (objects->data);
+	gck_list_unref_free (objects);
+
+	return result;
+}
+
+static gboolean
+check_dsa_attributes (GckAttributes *attributes)
+{
+	GckAttribute *prime;
+	GckAttribute *subprime;
+	GckAttribute *base;
+	GckAttribute *value;
+
+	prime = gck_attributes_find (attributes, CKA_PRIME);
+	subprime = gck_attributes_find (attributes, CKA_SUBPRIME);
+	base = gck_attributes_find (attributes, CKA_BASE);
+	value = gck_attributes_find (attributes, CKA_VALUE);
+
+	return (prime && !gck_attribute_is_invalid (prime) &&
+	        subprime && !gck_attribute_is_invalid (subprime) &&
+	        base && !gck_attribute_is_invalid (base) &&
+	        value && !gck_attribute_is_invalid (value));
+}
+
+static gboolean
+load_dsa_attributes (GckObject *object,
+                     GckAttributes *attributes,
+                     GCancellable *cancellable,
+                     GError **lerror)
+{
+	GError *error = NULL;
+	GckAttributes *loaded;
+	GckObject *publi;
+	gulong klass;
+
+	if (check_dsa_attributes (attributes))
+		return TRUE;
+
+	if (!gck_attributes_find_ulong (attributes, CKA_CLASS, &klass))
+		g_return_val_if_reached (FALSE);
+
+	/* If it's a private key, find the public one */
+	if (klass == CKO_PRIVATE_KEY)
+		publi = lookup_public_key (object, cancellable, lerror);
+
+	else
+		publi = g_object_ref (object);
+
+	if (!publi)
+		return FALSE;
+
+	loaded = gck_object_get (publi, cancellable, &error,
+	                         CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_VALUE,
+	                         GCK_INVALID);
+	g_object_unref (publi);
+
+	if (error != NULL) {
+		_gcr_debug ("couldn't load rsa attributes: %s", error->message);
+		g_propagate_error (lerror, error);
+		return FALSE;
+	}
+
+	/* We've made sure to load info from the public key, so change class */
+	gck_attributes_set_ulong (attributes, CKA_CLASS, CKO_PUBLIC_KEY);
+
+	gck_attributes_set_all (attributes, loaded);
+	gck_attributes_unref (loaded);
+
+	return check_dsa_attributes (attributes);
+}
+
+static gboolean
+load_attributes (GckObject *object,
+                 GckAttributes *attributes,
+                 GCancellable *cancellable,
+                 GError **lerror)
+{
+	gboolean ret = FALSE;
+	gulong klass;
+	gulong type;
+
+	if (!load_object_basics (object, attributes, cancellable,
+	                         &klass, &type, lerror))
+		return FALSE;
+
+	switch (klass) {
+
+	case CKO_CERTIFICATE:
+		switch (type) {
+		case CKC_X_509:
+			ret = load_x509_attributes (object, attributes, cancellable, lerror);
+			break;
+		default:
+			_gcr_debug ("unsupported certificate type: %lu", type);
+			break;
+		}
+		break;
+
+	case CKO_PUBLIC_KEY:
+	case CKO_PRIVATE_KEY:
+		switch (type) {
+		case CKK_RSA:
+			ret = load_rsa_attributes (object, attributes, cancellable, lerror);
+			break;
+		case CKK_DSA:
+			ret = load_dsa_attributes (object, attributes, cancellable, lerror);
+			break;
+		default:
+			_gcr_debug ("unsupported key type: %lu", type);
+			break;
+		}
+		break;
+
+	default:
+		_gcr_debug ("unsupported class: %lu", type);
+		break;
+	}
+
+	if (ret == FALSE && lerror != NULL && *lerror == NULL) {
+		g_set_error_literal (lerror, GCR_DATA_ERROR, GCR_ERROR_UNRECOGNIZED,
+		                     _("Unrecognized or unavailable attributes for key"));
+	}
+
+	return ret;
+}
+
+static gboolean
+check_attributes (GckAttributes *attributes)
+{
+	gulong klass;
+	gulong type;
+
+	if (!check_object_basics (attributes, &klass, &type))
+		return FALSE;
+
+	switch (klass) {
+
+	case CKO_CERTIFICATE:
+		switch (type) {
+		case CKC_X_509:
+			return check_x509_attributes (attributes);
+		default:
+			return FALSE;
+		}
+
+	case CKO_PUBLIC_KEY:
+	case CKO_PRIVATE_KEY:
+		switch (type) {
+		case CKK_RSA:
+			return check_rsa_attributes (attributes);
+		case CKK_DSA:
+			return check_dsa_attributes (attributes);
+		default:
+			return FALSE;
+		}
+
+	default:
+		return FALSE;
+	}
+}
+
+static GckAttributes *
+lookup_attributes (GckObject *object)
+{
+	GckObjectAttributes *oakey;
+
+	if (GCK_IS_OBJECT_ATTRIBUTES (object)) {
+		oakey = GCK_OBJECT_ATTRIBUTES (object);
+		return gck_object_attributes_get_attributes (oakey);
+	}
+
+	return NULL;
+}
+
+static void
+attributes_replace_with_copy_or_new (GckAttributes **attributes)
+{
+	g_assert (attributes);
+
+	if (*attributes) {
+		GckAttributes *copy = gck_attributes_dup (*attributes);
+		gck_attributes_unref (*attributes);
+		*attributes = copy;
+	} else {
+		*attributes = gck_attributes_new ();
+	}
+}
+
+GNode *
+_gcr_subject_public_key_load (GckObject *key,
+                              GCancellable *cancellable,
+                              GError **error)
+{
+	GckAttributes *attributes;
+	GNode *asn;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (key), NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	attributes = lookup_attributes (key);
+
+	if (!attributes || !check_attributes (attributes)) {
+		attributes_replace_with_copy_or_new (&attributes);
+
+		if (!load_attributes (key, attributes, cancellable, error)) {
+			gck_attributes_unref (attributes);
+			return NULL;
+		}
+	}
+
+	asn = _gcr_subject_public_key_for_attributes (attributes);
+	if (asn == NULL) {
+		g_set_error_literal (error, GCK_ERROR, CKR_TEMPLATE_INCONSISTENT,
+		                     _("Couldn't build public key"));
+	}
+
+	gck_attributes_unref (attributes);
+	return asn;
+}
+
+typedef struct {
+	GckObject *object;
+	GckAttributes *attributes;
+} LoadClosure;
+
+static void
+load_closure_free (gpointer data)
+{
+	LoadClosure *closure = data;
+	g_object_unref (closure->object);
+	gck_attributes_unref (closure->attributes);
+	g_slice_free (LoadClosure, closure);
+}
+
+static void
+thread_key_attributes (GSimpleAsyncResult *res,
+                       GObject *object,
+                       GCancellable *cancellable)
+{
+	LoadClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	GError *error = NULL;
+
+	if (!load_attributes (closure->object, closure->attributes, cancellable, &error))
+		g_simple_async_result_take_error (res, error);
+}
+
+void
+_gcr_subject_public_key_load_async (GckObject *key,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+	GSimpleAsyncResult *res;
+	LoadClosure *closure;
+
+	g_return_if_fail (GCK_IS_OBJECT (key));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	res = g_simple_async_result_new (NULL, callback, user_data,
+	                                 _gcr_subject_public_key_load_async);
+
+	closure = g_slice_new0 (LoadClosure);
+	closure->object = g_object_ref (key);
+	closure->attributes = lookup_attributes (key);
+	g_simple_async_result_set_op_res_gpointer (res, closure, load_closure_free);
+
+	if (closure->attributes && check_attributes (closure->attributes)) {
+		g_simple_async_result_complete_in_idle (res);
+		g_object_unref (res);
+		return;
+	}
+
+	attributes_replace_with_copy_or_new (&closure->attributes);
+	g_simple_async_result_run_in_thread (res, thread_key_attributes,
+	                                     G_PRIORITY_DEFAULT, cancellable);
+	g_object_unref (res);
+}
+
+GNode *
+_gcr_subject_public_key_load_finish (GAsyncResult *result,
+                                     GError **error)
+{
+	GSimpleAsyncResult *res;
+	LoadClosure *closure;
+	GNode *asn;
+
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
+	                      _gcr_subject_public_key_load_async), NULL);
+
+	res = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (res, error))
+		return NULL;
+
+	closure = g_simple_async_result_get_op_res_gpointer (res);
+	asn = _gcr_subject_public_key_for_attributes (closure->attributes);
+	if (asn == NULL) {
+		g_set_error_literal (error, GCK_ERROR, CKR_TEMPLATE_INCONSISTENT,
+		                     _("Couldn't build public key"));
+	}
+
+	return asn;
+}
+
+static gboolean
+rsa_subject_public_key_from_attributes (GckAttributes *attrs,
+                                        GNode *info_asn)
+{
+	GckAttribute *modulus;
+	GckAttribute *exponent;
+	GNode *key_asn;
+	GNode *params_asn;
+	EggBytes *key;
+	EggBytes *params;
+	EggBytes *usg;
+
+	_gcr_oids_init ();
+
+	modulus = gck_attributes_find (attrs, CKA_MODULUS);
+	exponent = gck_attributes_find (attrs, CKA_PUBLIC_EXPONENT);
+	if (modulus == NULL || exponent == NULL)
+		return FALSE;
+
+	key_asn = egg_asn1x_create (pk_asn1_tab, "RSAPublicKey");
+	g_return_val_if_fail (key_asn, FALSE);
+
+	params_asn = egg_asn1x_create (pk_asn1_tab, "RSAParameters");
+	g_return_val_if_fail (params_asn, FALSE);
+
+	usg = egg_bytes_new_with_free_func (modulus->value, modulus->length,
+	                                    gck_attributes_unref,
+	                                    gck_attributes_ref (attrs));
+	egg_asn1x_set_integer_as_usg (egg_asn1x_node (key_asn, "modulus", NULL), usg);
+	egg_bytes_unref (usg);
+
+	usg = egg_bytes_new_with_free_func (exponent->value, exponent->length,
+	                                    gck_attributes_unref,
+	                                    gck_attributes_ref (attrs));
+	egg_asn1x_set_integer_as_usg (egg_asn1x_node (key_asn, "publicExponent", NULL), usg);
+	egg_bytes_unref (usg);
+
+	key = egg_asn1x_encode (key_asn, NULL);
+	egg_asn1x_destroy (key_asn);
+
+	egg_asn1x_set_null (params_asn);
+
+	params = egg_asn1x_encode (params_asn, g_realloc);
+	egg_asn1x_destroy (params_asn);
+
+	egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
+	                           key, egg_bytes_get_size (key) * 8);
+
+	egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), GCR_OID_PKIX1_RSA);
+	egg_asn1x_set_element_raw (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params);
+
+	egg_bytes_unref (key);
+	egg_bytes_unref (params);
+	return TRUE;
+}
+
+static gboolean
+dsa_subject_public_key_from_private (GNode *key_asn,
+                                     GckAttribute *ap,
+                                     GckAttribute *aq,
+                                     GckAttribute *ag,
+                                     GckAttribute *ax)
+{
+	gcry_mpi_t mp, mq, mg, mx, my;
+	size_t n_buffer;
+	gcry_error_t gcry;
+	unsigned char *buffer;
+
+	gcry = gcry_mpi_scan (&mp, GCRYMPI_FMT_USG, ap->value, ap->length, NULL);
+	g_return_val_if_fail (gcry == 0, FALSE);
+
+	gcry = gcry_mpi_scan (&mq, GCRYMPI_FMT_USG, aq->value, aq->length, NULL);
+	g_return_val_if_fail (gcry == 0, FALSE);
+
+	gcry = gcry_mpi_scan (&mg, GCRYMPI_FMT_USG, ag->value, ag->length, NULL);
+	g_return_val_if_fail (gcry == 0, FALSE);
+
+	gcry = gcry_mpi_scan (&mx, GCRYMPI_FMT_USG, ax->value, ax->length, NULL);
+	g_return_val_if_fail (gcry == 0, FALSE);
+
+	/* Calculate the public part from the private */
+	my = gcry_mpi_snew (gcry_mpi_get_nbits (mx));
+	g_return_val_if_fail (my, FALSE);
+	gcry_mpi_powm (my, mg, mx, mp);
+
+	gcry = gcry_mpi_aprint (GCRYMPI_FMT_STD, &buffer, &n_buffer, my);
+	g_return_val_if_fail (gcry == 0, FALSE);
+	egg_asn1x_take_integer_as_raw (key_asn, egg_bytes_new_with_free_func (buffer, n_buffer,
+	                                                                      gcry_free, buffer));
+
+	gcry_mpi_release (mp);
+	gcry_mpi_release (mq);
+	gcry_mpi_release (mg);
+	gcry_mpi_release (mx);
+	gcry_mpi_release (my);
+
+	return TRUE;
+}
+
+static gboolean
+dsa_subject_public_key_from_attributes (GckAttributes *attrs,
+                                        gulong klass,
+                                        GNode *info_asn)
+{
+	GckAttribute *value, *g, *q, *p;
+	GNode *key_asn, *params_asn;
+	EggBytes *key;
+	EggBytes *params;
+
+	_gcr_oids_init ();
+
+	p = gck_attributes_find (attrs, CKA_PRIME);
+	q = gck_attributes_find (attrs, CKA_SUBPRIME);
+	g = gck_attributes_find (attrs, CKA_BASE);
+	value = gck_attributes_find (attrs, CKA_VALUE);
+
+	if (p == NULL || q == NULL || g == NULL || value == NULL)
+		return FALSE;
+
+	key_asn = egg_asn1x_create (pk_asn1_tab, "DSAPublicPart");
+	g_return_val_if_fail (key_asn, FALSE);
+
+	params_asn = egg_asn1x_create (pk_asn1_tab, "DSAParameters");
+	g_return_val_if_fail (params_asn, FALSE);
+
+	egg_asn1x_take_integer_as_usg (egg_asn1x_node (params_asn, "p", NULL),
+	                               egg_bytes_new_with_free_func (p->value, p->length,
+	                                                             gck_attributes_unref,
+	                                                             gck_attributes_ref (attrs)));
+	egg_asn1x_take_integer_as_usg (egg_asn1x_node (params_asn, "q", NULL),
+	                               egg_bytes_new_with_free_func (q->value, q->length,
+	                                                             gck_attributes_unref,
+	                                                             gck_attributes_ref (attrs)));
+	egg_asn1x_take_integer_as_usg (egg_asn1x_node (params_asn, "g", NULL),
+	                               egg_bytes_new_with_free_func (g->value, g->length,
+	                                                             gck_attributes_unref,
+	                                                             gck_attributes_ref (attrs)));
+
+	/* Are these attributes for a public or private key? */
+	if (klass == CKO_PRIVATE_KEY) {
+
+		/* We need to calculate the public from the private key */
+		if (!dsa_subject_public_key_from_private (key_asn, p, q, g, value))
+			g_return_val_if_reached (FALSE);
+
+	} else if (klass == CKO_PUBLIC_KEY) {
+		egg_asn1x_take_integer_as_usg (key_asn,
+		                               egg_bytes_new_with_free_func (value->value, value->length,
+		                                                             gck_attributes_unref,
+		                                                             gck_attributes_ref (attrs)));
+	} else {
+		g_assert_not_reached ();
+	}
+
+	key = egg_asn1x_encode (key_asn, NULL);
+	egg_asn1x_destroy (key_asn);
+
+	params = egg_asn1x_encode (params_asn, NULL);
+	egg_asn1x_destroy (params_asn);
+
+	egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
+	                           key, egg_bytes_get_size (key) * 8);
+	egg_asn1x_set_element_raw (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params);
+
+	egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), GCR_OID_PKIX1_DSA);
+
+	egg_bytes_unref (key);
+	egg_bytes_unref (params);
+	return TRUE;
+}
+
+static GNode *
+cert_subject_public_key_from_attributes (GckAttributes *attributes)
+{
+	GckAttribute *attr;
+	EggBytes *bytes;
+	GNode *cert;
+	GNode *asn;
+
+	attr = gck_attributes_find (attributes, CKA_VALUE);
+	if (attr == NULL) {
+		_gcr_debug ("no value attribute for certificate");
+		return NULL;
+	}
+
+	bytes = egg_bytes_new_with_free_func (attr->value, attr->length,
+	                                      gck_attributes_unref,
+	                                      gck_attributes_ref (attributes));
+	cert = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", bytes);
+	egg_bytes_unref (bytes);
+
+	if (cert == NULL) {
+		_gcr_debug ("couldn't parse certificate value");
+		return NULL;
+	}
+
+	asn = egg_asn1x_node (cert, "tbsCertificate", "subjectPublicKeyInfo", NULL);
+	g_return_val_if_fail (asn != NULL, NULL);
+
+	/* Remove the subject public key out of the certificate */
+	g_node_unlink (asn);
+	egg_asn1x_destroy (cert);
+
+	return asn;
+}
+
+GNode *
+_gcr_subject_public_key_for_attributes (GckAttributes *attributes)
+{
+	gboolean ret = FALSE;
+	gulong key_type;
+	gulong klass;
+	GNode *asn;
+
+	if (!gck_attributes_find_ulong (attributes, CKA_CLASS, &klass)) {
+		_gcr_debug ("no class in attributes");
+		return NULL;
+	}
+
+	if (klass == CKO_CERTIFICATE) {
+		return cert_subject_public_key_from_attributes (attributes);
+
+	} else if (klass == CKO_PUBLIC_KEY || klass == CKO_PRIVATE_KEY) {
+		if (!gck_attributes_find_ulong (attributes, CKA_KEY_TYPE, &key_type)) {
+			_gcr_debug ("no key type in attributes");
+			return NULL;
+		}
+
+		asn = egg_asn1x_create (pkix_asn1_tab, "SubjectPublicKeyInfo");
+		g_return_val_if_fail (asn, NULL);
+
+		if (key_type == CKK_RSA) {
+			ret = rsa_subject_public_key_from_attributes (attributes, asn);
+
+		} else if (key_type == CKK_DSA) {
+			ret = dsa_subject_public_key_from_attributes (attributes, klass, asn);
+
+		} else {
+			_gcr_debug ("unsupported key type: %lu", key_type);
+			ret = FALSE;
+		}
+
+
+		if (ret == FALSE) {
+			egg_asn1x_destroy (asn);
+			asn = NULL;
+		}
+	}
+
+	return asn;
+}
+
+static guint
+calculate_rsa_key_size (EggBytes *data)
+{
+	GNode *asn;
+	EggBytes *content;
+	guint key_size;
+
+	asn = egg_asn1x_create_and_decode (pk_asn1_tab, "RSAPublicKey", data);
+	g_return_val_if_fail (asn, 0);
+
+	content = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "modulus", NULL));
+	if (!content)
+		g_return_val_if_reached (0);
+
+	egg_asn1x_destroy (asn);
+
+	/* Removes the complement */
+	key_size = (egg_bytes_get_size (content) / 2) * 2 * 8;
+
+	egg_bytes_unref (content);
+	return key_size;
+}
+
+static guint
+calculate_dsa_params_size (EggBytes *data)
+{
+	GNode *asn;
+	EggBytes *content;
+	guint key_size;
+
+	asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", data);
+	g_return_val_if_fail (asn, 0);
+
+	content = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "p", NULL));
+	if (!content)
+		g_return_val_if_reached (0);
+
+	egg_asn1x_destroy (asn);
+
+	/* Removes the complement */
+	key_size = (egg_bytes_get_size (content) / 2) * 2 * 8;
+
+	egg_bytes_unref (content);
+	return key_size;
+}
+
+guint
+_gcr_subject_public_key_calculate_size (GNode *subject_public_key)
+{
+	EggBytes *key;
+	guint key_size = 0;
+	guint n_bits;
+	GQuark oid;
+
+	/* Figure out the algorithm */
+	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (subject_public_key,
+	                                                  "algorithm", "algorithm", NULL));
+	g_return_val_if_fail (oid != 0, 0);
+
+	/* RSA keys are stored in the main subjectPublicKey field */
+	if (oid == GCR_OID_PKIX1_RSA) {
+		key = egg_asn1x_get_bits_as_raw (egg_asn1x_node (subject_public_key, "subjectPublicKey", NULL), &n_bits);
+		g_return_val_if_fail (key != NULL, 0);
+		key_size = calculate_rsa_key_size (key);
+		egg_bytes_unref (key);
+
+	/* The DSA key size is discovered by the prime in params */
+	} else if (oid == GCR_OID_PKIX1_DSA) {
+		key = egg_asn1x_get_element_raw (egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL));
+		key_size = calculate_dsa_params_size (key);
+		egg_bytes_unref (key);
+
+	} else {
+		g_message ("unsupported key algorithm: %s", g_quark_to_string (oid));
+	}
+
+	return key_size;
+}
diff --git a/gcr/gcr-subject-public-key.h b/gcr/gcr-subject-public-key.h
new file mode 100644
index 0000000..bfee315
--- /dev/null
+++ b/gcr/gcr-subject-public-key.h
@@ -0,0 +1,55 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> or <gcr/gcr-base.h> can be included directly."
+#endif
+
+#ifndef GCR_SUBJECT_PUBLIC_KEY_H
+#define GCR_SUBJECT_PUBLIC_KEY_H
+
+#include <glib.h>
+
+#include <gck/gck.h>
+
+G_BEGIN_DECLS
+
+GNode *        _gcr_subject_public_key_for_attributes   (GckAttributes *attributes);
+
+GNode *        _gcr_subject_public_key_load             (GckObject *key,
+                                                         GCancellable *cancellable,
+                                                         GError **error);
+
+void           _gcr_subject_public_key_load_async       (GckObject *key,
+                                                         GCancellable *cancellable,
+                                                         GAsyncReadyCallback callback,
+                                                         gpointer user_data);
+
+GNode *        _gcr_subject_public_key_load_finish      (GAsyncResult *result,
+                                                         GError **error);
+
+guint          _gcr_subject_public_key_calculate_size   (GNode *subject_public_key);
+
+G_END_DECLS
+
+#endif /* GCR_CERTIFICATE_H */
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
index dd522be..e8a01c5 100644
--- a/gcr/gcr-types.h
+++ b/gcr/gcr-types.h
@@ -75,6 +75,8 @@ typedef enum {
 	GCR_FORMAT_DER_PRIVATE_KEY_RSA,
 	GCR_FORMAT_DER_PRIVATE_KEY_DSA,
 
+	GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY = 150,
+
 	GCR_FORMAT_DER_CERTIFICATE_X509 = 200,
 
 	GCR_FORMAT_DER_PKCS7 = 300,
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index c133c17..5e611f7 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -27,6 +27,7 @@ TEST_PROGS = \
 	test-simple-certificate \
 	test-certificate \
 	test-certificate-chain \
+	test-subject-public-key \
 	test-fingerprint \
 	test-pkcs11-certificate \
 	test-openpgp \
diff --git a/gcr/tests/files/client.spk b/gcr/tests/files/client.spk
new file mode 100644
index 0000000..09ab6b9
Binary files /dev/null and b/gcr/tests/files/client.spk differ
diff --git a/gcr/tests/files/generic-dsa.spk b/gcr/tests/files/generic-dsa.spk
new file mode 100644
index 0000000..161391c
Binary files /dev/null and b/gcr/tests/files/generic-dsa.spk differ
diff --git a/gcr/tests/test-subject-public-key.c b/gcr/tests/test-subject-public-key.c
new file mode 100644
index 0000000..0e99563
--- /dev/null
+++ b/gcr/tests/test-subject-public-key.c
@@ -0,0 +1,721 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+   Copyright (C) 2010 Collabora Ltd
+
+   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 <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "gcr/gcr-base.h"
+#include "gcr/gcr-subject-public-key.h"
+
+#include "gck/gck-mock.h"
+#include "gck/gck-test.h"
+
+#include "egg/egg-asn1x.h"
+#include "egg/egg-asn1-defs.h"
+#include "egg/egg-testing.h"
+
+#include <glib.h>
+
+#include <errno.h>
+
+typedef struct {
+	const gchar *name;
+	const gchar *basename;
+	guint key_size;
+} TestFixture;
+
+typedef struct {
+	EggBytes *crt_data;
+	GckAttributes *crt_attrs;
+	EggBytes *key_data;
+	GckAttributes *prv_attrs;
+	EggBytes *spk_data;
+	GckAttributes *pub_attrs;
+} TestAttributes;
+
+static void
+on_parser_parsed (GcrParser *parser,
+                  gpointer user_data)
+{
+	GckAttributes **attrs = user_data;
+	g_assert (*attrs == NULL);
+	*attrs = gcr_parser_get_parsed_attributes (parser);
+	g_assert (*attrs != NULL);
+	gck_attributes_ref (*attrs);
+}
+
+static GckAttributes *
+parse_attributes (EggBytes *data)
+{
+	GcrParser *parser;
+	GckAttributes *attrs = NULL;
+	GError *error = NULL;
+
+	parser = gcr_parser_new ();
+	g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), &attrs);
+	gcr_parser_parse_data (parser, egg_bytes_get_data (data),
+	                       egg_bytes_get_size (data), &error);
+	g_assert_no_error (error);
+	g_object_unref (parser);
+
+	g_assert (attrs);
+	return attrs;
+}
+
+static void
+setup_attributes (TestAttributes *test,
+                  gconstpointer data)
+{
+	const TestFixture *fixture = data;
+	GError *error = NULL;
+	gchar *contents;
+	gchar *filename;
+	gsize length;
+	gulong klass;
+
+	filename = g_strdup_printf (SRCDIR "/files/%s.crt", fixture->basename);
+	g_file_get_contents (filename, &contents, &length, &error);
+	g_assert_no_error (error);
+	test->crt_data = egg_bytes_new_take (contents, length);
+	test->crt_attrs = parse_attributes (test->crt_data);
+	g_assert (gck_attributes_find_ulong (test->crt_attrs, CKA_CLASS, &klass));
+	gck_assert_cmpulong (klass, ==, CKO_CERTIFICATE);
+	g_free (filename);
+
+	filename = g_strdup_printf (SRCDIR "/files/%s.key", fixture->basename);
+	g_file_get_contents (filename, &contents, &length, &error);
+	g_assert_no_error (error);
+	test->key_data = egg_bytes_new_take (contents, length);
+	test->prv_attrs = parse_attributes (test->key_data);
+	g_assert (gck_attributes_find_ulong (test->prv_attrs, CKA_CLASS, &klass));
+	gck_assert_cmpulong (klass, ==, CKO_PRIVATE_KEY);
+	g_free (filename);
+
+	filename = g_strdup_printf (SRCDIR "/files/%s.spk", fixture->basename);
+	g_file_get_contents (filename, &contents, &length, &error);
+	g_assert_no_error (error);
+	test->spk_data = egg_bytes_new_take (contents, length);
+	test->pub_attrs = parse_attributes (test->spk_data);
+	g_assert (gck_attributes_find_ulong (test->pub_attrs, CKA_CLASS, &klass));
+	gck_assert_cmpulong (klass, ==, CKO_PUBLIC_KEY);
+	g_free (filename);
+}
+
+static void
+teardown_attributes (TestAttributes *test,
+                     gconstpointer unused)
+{
+	egg_bytes_unref (test->crt_data);
+	egg_bytes_unref (test->key_data);
+	egg_bytes_unref (test->spk_data);
+	gck_attributes_unref (test->crt_attrs);
+	gck_attributes_unref (test->prv_attrs);
+	gck_attributes_unref (test->pub_attrs);
+}
+
+static void
+perform_for_attributes (TestAttributes *test,
+                        GckAttributes *attrs)
+{
+	GNode *info;
+	EggBytes *data;
+
+	info = _gcr_subject_public_key_for_attributes (attrs);
+	g_assert (info != NULL);
+
+	data = egg_asn1x_encode (info, NULL);
+	egg_assert_cmpbytes (data, ==, egg_bytes_get_data (test->spk_data),
+	                               egg_bytes_get_size (test->spk_data));
+
+	egg_bytes_unref (data);
+	egg_asn1x_destroy (info);
+
+}
+
+static void
+test_for_cert_attributes (TestAttributes *test,
+                          gconstpointer unused)
+{
+	perform_for_attributes (test, test->crt_attrs);
+}
+
+static void
+test_for_private_key_attributes (TestAttributes *test,
+                                 gconstpointer unused)
+{
+	perform_for_attributes (test, test->prv_attrs);
+}
+
+static void
+test_for_public_key_attributes (TestAttributes *test,
+                                gconstpointer unused)
+{
+	perform_for_attributes (test, test->pub_attrs);
+}
+
+static void
+perform_calculate_size (TestAttributes *test,
+                        GckAttributes *attrs,
+                        const TestFixture *fixture)
+{
+	GNode *info;
+	guint size;
+
+	info = _gcr_subject_public_key_for_attributes (attrs);
+	g_assert (info != NULL);
+
+	/* TODO: until encoding, we don't have readable attributes */
+	egg_bytes_unref (egg_asn1x_encode (info, NULL));
+
+	size = _gcr_subject_public_key_calculate_size (info);
+	g_assert_cmpuint (size, ==, fixture->key_size);
+
+	egg_asn1x_destroy (info);
+
+}
+
+static void
+test_certificate_calculate_size (TestAttributes *test,
+                                 gconstpointer fixture)
+{
+	perform_calculate_size (test, test->crt_attrs, fixture);
+}
+
+static void
+test_public_key_calculate_size (TestAttributes *test,
+                                gconstpointer fixture)
+{
+	perform_calculate_size (test, test->pub_attrs, fixture);
+}
+
+static void
+test_private_key_calculate_size (TestAttributes *test,
+                                 gconstpointer fixture)
+{
+	perform_calculate_size (test, test->prv_attrs, fixture);
+}
+
+typedef struct {
+	CK_FUNCTION_LIST funcs;
+	GckModule *module;
+	GckSession *session;
+} TestModule;
+
+static void
+setup_module (TestModule *test,
+               gconstpointer unused)
+{
+	CK_FUNCTION_LIST_PTR f;
+	GError *error = NULL;
+	GckSlot *slot;
+	CK_RV rv;
+
+	rv = gck_mock_C_GetFunctionList (&f);
+	gck_assert_cmprv (rv, ==, CKR_OK);
+	memcpy (&test->funcs, f, sizeof (test->funcs));
+
+	/* Open a session */
+	rv = (test->funcs.C_Initialize) (NULL);
+	gck_assert_cmprv (rv, ==, CKR_OK);
+
+	test->module = gck_module_new (&test->funcs);
+	slot = gck_slot_from_handle (test->module, GCK_MOCK_SLOT_ONE_ID);
+	test->session = gck_session_open (slot, GCK_SESSION_READ_ONLY, NULL, NULL, &error);
+	g_assert_no_error (error);
+
+	g_object_unref (slot);
+}
+
+static void
+teardown_module (TestModule *test,
+                 gconstpointer fixture)
+{
+	CK_RV rv;
+
+	g_object_unref (test->session);
+	egg_assert_not_object (test->session);
+
+	g_object_unref (test->module);
+	egg_assert_not_object (test->module);
+
+	rv = (test->funcs.C_Finalize) (NULL);
+	gck_assert_cmprv (rv, ==, CKR_OK);
+}
+
+typedef struct {
+	TestAttributes at;
+	TestModule mo;
+	GckObject *crt_object;
+	GckObject *pub_object;
+	GckObject *prv_object;
+} TestLoading;
+
+static void
+setup_loading (TestLoading *test,
+               gconstpointer fixture)
+{
+	const gchar *id = "test-id";
+	gulong handle;
+
+	setup_attributes (&test->at, fixture);
+	setup_module (&test->mo, NULL);
+
+	gck_attributes_add_string (test->at.crt_attrs, CKA_ID, id);
+	handle = gck_mock_module_take_object (gck_attributes_ref (test->at.crt_attrs));
+	test->crt_object = gck_object_from_handle (test->mo.session, handle);
+
+	gck_attributes_add_string (test->at.pub_attrs, CKA_ID, id);
+	handle = gck_mock_module_take_object (gck_attributes_ref (test->at.pub_attrs));
+	test->pub_object = gck_object_from_handle (test->mo.session, handle);
+
+	gck_attributes_add_string (test->at.prv_attrs, CKA_ID, id);
+	handle = gck_mock_module_take_object (gck_attributes_ref (test->at.prv_attrs));
+	test->prv_object = gck_object_from_handle (test->mo.session, handle);
+}
+
+static void
+teardown_loading (TestLoading *test,
+                  gconstpointer fixture)
+{
+	g_object_unref (test->crt_object);
+	egg_assert_not_object (test->crt_object);
+
+	g_object_unref (test->prv_object);
+	egg_assert_not_object (test->prv_object);
+
+	g_object_unref (test->pub_object);
+	egg_assert_not_object (test->pub_object);
+
+	teardown_module (&test->mo, NULL);
+	teardown_attributes (&test->at, fixture);
+}
+
+static void
+perform_load (TestLoading *test,
+              GckObject *object)
+{
+	GError *error = NULL;
+	EggBytes *data;
+	GNode *info;
+
+	info = _gcr_subject_public_key_load (object, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (info != NULL);
+
+	data = egg_asn1x_encode (info, NULL);
+	egg_assert_cmpbytes (data, ==, egg_bytes_get_data (test->at.spk_data),
+	                               egg_bytes_get_size (test->at.spk_data));
+
+	egg_bytes_unref (data);
+	egg_asn1x_destroy (info);
+}
+
+static void
+test_certificate_load (TestLoading *test,
+                       gconstpointer unused)
+{
+	perform_load (test, test->crt_object);
+}
+
+static void
+test_public_key_load (TestLoading *test,
+                      gconstpointer unused)
+{
+	perform_load (test, test->pub_object);
+}
+
+static void
+test_private_key_load (TestLoading *test,
+                       gconstpointer unused)
+{
+	perform_load (test, test->prv_object);
+}
+
+static void
+on_async_result (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
+{
+	GAsyncResult **ret = user_data;
+	g_assert (ret != NULL);
+	g_assert (*ret == NULL);
+	g_assert (G_IS_ASYNC_RESULT (result));
+	*ret = g_object_ref (result);
+	egg_test_wait_stop ();
+}
+
+static void
+perform_load_async (TestLoading *test,
+                    GckObject *object)
+{
+	GAsyncResult *result = NULL;
+	GError *error = NULL;
+	EggBytes *data;
+	GNode *info;
+
+	_gcr_subject_public_key_load_async (object, NULL, on_async_result, &result);
+	g_assert (result == NULL);
+	egg_test_wait ();
+
+	g_assert (result != NULL);
+	info = _gcr_subject_public_key_load_finish (result, &error);
+	g_assert_no_error (error);
+	g_assert (info != NULL);
+	g_object_unref (result);
+
+	data = egg_asn1x_encode (info, NULL);
+	egg_assert_cmpbytes (data, ==, egg_bytes_get_data (test->at.spk_data),
+	                               egg_bytes_get_size (test->at.spk_data));
+
+	egg_bytes_unref (data);
+	egg_asn1x_destroy (info);
+}
+
+static void
+test_certificate_load_async (TestLoading *test,
+                             gconstpointer unused)
+{
+	perform_load_async (test, test->crt_object);
+}
+
+static void
+test_public_key_load_async (TestLoading *test,
+                            gconstpointer unused)
+{
+	perform_load_async (test, test->pub_object);
+}
+
+static void
+test_private_key_load_async (TestLoading *test,
+                             gconstpointer unused)
+{
+	perform_load_async (test, test->prv_object);
+}
+
+enum { PROP_ATTRIBUTES = 1 };
+typedef struct { GckObject parent; GckAttributes *attrs; } MockObject;
+typedef struct { GckObjectClass parent; } MockObjectClass;
+
+GType mock_object_get_type (void) G_GNUC_CONST;
+static void mock_object_attributes_init (GckObjectAttributesIface *iface);
+G_DEFINE_TYPE_WITH_CODE (MockObject, mock_object, GCK_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCK_TYPE_OBJECT_ATTRIBUTES, mock_object_attributes_init)
+);
+
+static void
+mock_object_init (MockObject *self)
+{
+
+}
+
+static void
+mock_object_get_property (GObject *obj,
+                          guint prop_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+	g_assert (prop_id == PROP_ATTRIBUTES);
+	g_value_set_boxed (value, ((MockObject *)obj)->attrs);
+}
+
+static void
+mock_object_set_property (GObject *obj,
+                          guint prop_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+	g_assert (prop_id == PROP_ATTRIBUTES);
+	((MockObject *)obj)->attrs = g_value_dup_boxed (value);
+}
+
+static void
+mock_object_finalize (GObject *obj)
+{
+	MockObject *self = (MockObject *)obj;
+	gck_attributes_unref (self->attrs);
+	G_OBJECT_CLASS (mock_object_parent_class)->finalize (obj);
+}
+
+static void
+mock_object_class_init (MockObjectClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	gobject_class->get_property = mock_object_get_property;
+	gobject_class->set_property = mock_object_set_property;
+	gobject_class->finalize = mock_object_finalize;
+	g_object_class_override_property (gobject_class, PROP_ATTRIBUTES, "attributes");
+}
+
+static void
+mock_object_attributes_init (GckObjectAttributesIface *iface)
+{
+	iface->attribute_types = NULL;
+	iface->n_attribute_types = 0;
+}
+
+static void
+perform_load_already (TestLoading *test,
+                      GckAttributes *attributes)
+{
+	const gulong INVALID = 0xFFF00FF; /* invalid handle, should not be used */
+	GckObject *object;
+	GError *error = NULL;
+	EggBytes *data;
+	GNode *info;
+
+	object = g_object_new (mock_object_get_type (),
+	                       "module", test->mo.module,
+	                       "session", test->mo.session,
+	                       "handle", INVALID,
+	                       "attributes", attributes,
+	                       NULL);
+
+	info = _gcr_subject_public_key_load (object, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (info != NULL);
+
+	data = egg_asn1x_encode (info, NULL);
+	egg_assert_cmpbytes (data, ==, egg_bytes_get_data (test->at.spk_data),
+	                               egg_bytes_get_size (test->at.spk_data));
+
+	egg_bytes_unref (data);
+	egg_asn1x_destroy (info);
+	g_object_unref (object);
+}
+
+static void
+test_certificate_load_already (TestLoading *test,
+                               gconstpointer unused)
+{
+	perform_load_already (test, test->at.crt_attrs);
+}
+
+static void
+test_public_key_load_already (TestLoading *test,
+                              gconstpointer unused)
+{
+	perform_load_already (test, test->at.pub_attrs);
+}
+
+static void
+test_private_key_load_already (TestLoading *test,
+                               gconstpointer unused)
+{
+	perform_load_already (test, test->at.prv_attrs);
+}
+
+static void
+perform_load_partial (TestLoading *test,
+                      GckObject *original,
+                      GckAttributes *attributes)
+{
+	GckAttributes *partial;
+	GckObject *object;
+	GError *error = NULL;
+	EggBytes *data;
+	GNode *info;
+	guint i;
+
+	partial = gck_attributes_new ();
+	for (i = 0; i < gck_attributes_count (attributes); i += 2)
+		gck_attributes_add (partial, gck_attributes_at (attributes, i));
+
+	object = g_object_new (mock_object_get_type (),
+	                       "module", test->mo.module,
+	                       "session", test->mo.session,
+	                       "handle", gck_object_get_handle (original),
+	                       "attributes", partial,
+	                       NULL);
+	gck_attributes_unref (partial);
+
+	info = _gcr_subject_public_key_load (object, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (info != NULL);
+
+	data = egg_asn1x_encode (info, NULL);
+	egg_assert_cmpbytes (data, ==, egg_bytes_get_data (test->at.spk_data),
+	                               egg_bytes_get_size (test->at.spk_data));
+
+	egg_bytes_unref (data);
+	egg_asn1x_destroy (info);
+	g_object_unref (object);
+}
+
+static void
+test_certificate_load_partial (TestLoading *test,
+                               gconstpointer unused)
+{
+	perform_load_partial (test, test->crt_object, test->at.crt_attrs);
+}
+
+static void
+test_public_key_load_partial (TestLoading *test,
+                              gconstpointer unused)
+{
+	perform_load_partial (test, test->pub_object, test->at.pub_attrs);
+}
+
+static void
+test_private_key_load_partial (TestLoading *test,
+                               gconstpointer unused)
+{
+	perform_load_partial (test, test->prv_object, test->at.prv_attrs);
+}
+
+static void
+test_load_failure_lookup (TestModule *test,
+                          gconstpointer fixture)
+{
+	const gulong INVALID = 0xFFF00FF; /* invalid handle, should fail */
+	GckObject *object;
+	GError *error = NULL;
+	GNode *info;
+
+	object = g_object_new (mock_object_get_type (),
+	                       "module", test->module,
+	                       "session", test->session,
+	                       "handle", INVALID,
+	                       NULL);
+
+	info = _gcr_subject_public_key_load (object, NULL, &error);
+	g_assert_error (error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID);
+	g_assert (info == NULL);
+	g_error_free (error);
+
+	g_object_unref (object);
+}
+
+static void
+test_load_failure_build (TestModule *test,
+                         gconstpointer fixture)
+{
+	GckAttributes *attributes;
+	const gulong INVALID = 0xFFF00FF; /* invalid handle, shouldn't be used */
+	GckObject *object;
+	GError *error = NULL;
+	GNode *info;
+
+	attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (attributes, CKA_CLASS, CKO_CERTIFICATE);
+	gck_attributes_add_ulong (attributes, CKA_CERTIFICATE_TYPE, CKC_X_509);
+	gck_attributes_add_string (attributes, CKA_VALUE, "invalid value");
+
+	object = g_object_new (mock_object_get_type (),
+	                       "module", test->module,
+	                       "session", test->session,
+	                       "handle", INVALID,
+	                       "attributes", attributes,
+	                       NULL);
+
+	gck_attributes_unref (attributes);
+
+	info = _gcr_subject_public_key_load (object, NULL, &error);
+	g_assert_error (error, GCK_ERROR, CKR_TEMPLATE_INCONSISTENT);
+	g_assert (info == NULL);
+	g_error_free (error);
+
+	g_object_unref (object);
+}
+
+static const TestFixture FIXTURES[] = {
+	{ "rsa", "client", 2048 },
+	{ "dsa", "generic-dsa", 1024 },
+};
+
+static GPtrArray *test_names = NULL;
+
+static const gchar *
+test_name (const gchar *format,
+           const gchar *basename)
+{
+	gchar *name = g_strdup_printf (format, basename);
+	g_ptr_array_add (test_names, name);
+	return name;
+}
+
+int
+main (int argc, char **argv)
+{
+	const TestFixture *fixture;
+	gint ret;
+	guint i;
+
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	test_names = g_ptr_array_new_with_free_func (g_free);
+
+	for (i = 0; i < G_N_ELEMENTS (FIXTURES); i++) {
+		fixture = &FIXTURES[i];
+
+		g_test_add (test_name ("/gcr/subject-public-key/%s/cert-attributes", fixture->name), TestAttributes, fixture,
+		            setup_attributes, test_for_cert_attributes, teardown_attributes);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/public-key-attributes", fixture->name), TestAttributes, fixture,
+		            setup_attributes, test_for_public_key_attributes, teardown_attributes);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/private-key-attributes", fixture->name), TestAttributes, fixture,
+		            setup_attributes, test_for_private_key_attributes, teardown_attributes);
+
+		g_test_add (test_name ("/gcr/subject-public-key/%s/certificate-size", fixture->name), TestAttributes, fixture,
+		            setup_attributes, test_certificate_calculate_size, teardown_attributes);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/public-key-size", fixture->name), TestAttributes, fixture,
+		            setup_attributes, test_public_key_calculate_size, teardown_attributes);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/private-key-size", fixture->name), TestAttributes, fixture,
+		            setup_attributes, test_private_key_calculate_size, teardown_attributes);
+
+		g_test_add (test_name ("/gcr/subject-public-key/%s/certificate-load", fixture->name), TestLoading, fixture,
+		            setup_loading, test_certificate_load, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/public-key-load", fixture->name), TestLoading, fixture,
+		            setup_loading, test_public_key_load, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/private-key-load", fixture->name), TestLoading, fixture,
+		            setup_loading, test_private_key_load, teardown_loading);
+
+		g_test_add (test_name ("/gcr/subject-public-key/%s/certificate-load-async", fixture->name), TestLoading, fixture,
+		            setup_loading, test_certificate_load_async, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/public-key-load-async", fixture->name), TestLoading, fixture,
+		            setup_loading, test_public_key_load_async, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/private-key-load-async", fixture->name), TestLoading, fixture,
+		            setup_loading, test_private_key_load_async, teardown_loading);
+
+		g_test_add (test_name ("/gcr/subject-public-key/%s/certificate-load-already", fixture->name), TestLoading, fixture,
+		            setup_loading, test_certificate_load_already, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/public-key-load-already", fixture->name), TestLoading, fixture,
+		            setup_loading, test_public_key_load_already, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/private-key-load-already", fixture->name), TestLoading, fixture,
+		            setup_loading, test_private_key_load_already, teardown_loading);
+
+		g_test_add (test_name ("/gcr/subject-public-key/%s/certificate-load-partial", fixture->name), TestLoading, fixture,
+		            setup_loading, test_certificate_load_partial, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/public-key-load-partial", fixture->name), TestLoading, fixture,
+		            setup_loading, test_public_key_load_partial, teardown_loading);
+		g_test_add (test_name ("/gcr/subject-public-key/%s/private-key-load-partial", fixture->name), TestLoading, fixture,
+		            setup_loading, test_private_key_load_partial, teardown_loading);
+	}
+
+	g_test_add ("/gcr/subject-public-key/load-failure-lookup", TestModule, NULL,
+	            setup_module, test_load_failure_lookup, teardown_module);
+	g_test_add ("/gcr/subject-public-key/load-failure-build", TestModule, NULL,
+	            setup_module, test_load_failure_build, teardown_module);
+
+	ret = egg_tests_run_with_loop ();
+
+	g_ptr_array_free (test_names, TRUE);
+	return ret;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c5c8b38..b8f5733 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -24,6 +24,7 @@ gcr/gcr-parser.c
 gcr/gcr-pkcs11-import-dialog.c
 [type: gettext/glade]gcr/gcr-pkcs11-import-dialog.ui
 gcr/gcr-pkcs11-import-interaction.c
+gcr/gcr-subject-public-key.c
 gcr/gcr-trust.c
 [type: gettext/glade]gcr/gcr-unlock-options-widget.ui
 gcr/gcr-unlock-renderer.c



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