gcr: Implement functions for loading and using SubjectPublicKeyInfo
authorStef Walter <stefw@collabora.co.uk>
Fri, 18 Nov 2011 12:22:40 +0000 (13:22 +0100)
committerStef Walter <stefw@collabora.co.uk>
Wed, 23 Nov 2011 08:10:52 +0000 (09:10 +0100)
 * 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

23 files changed:
docs/reference/gcr/gcr-sections.txt
egg/egg-asn1x.c
egg/egg-testing.h
gck/gck-mock.c
gcr/Makefile.am
gcr/gcr-base.symbols
gcr/gcr-certificate-req-renderer.c
gcr/gcr-certificate.c
gcr/gcr-debug.c
gcr/gcr-debug.h
gcr/gcr-fingerprint.c
gcr/gcr-fingerprint.h
gcr/gcr-key-size.c [deleted file]
gcr/gcr-key-size.h [deleted file]
gcr/gcr-parser.c
gcr/gcr-subject-public-key.c [new file with mode: 0644]
gcr/gcr-subject-public-key.h [new file with mode: 0644]
gcr/gcr-types.h
gcr/tests/Makefile.am
gcr/tests/files/client.spk [new file with mode: 0644]
gcr/tests/files/generic-dsa.spk [new file with mode: 0644]
gcr/tests/test-subject-public-key.c [new file with mode: 0644]
po/POTFILES.in

index 9e52421..adcb740 100644 (file)
@@ -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>
 
index b7890e8..fc80d8f 100644 (file)
@@ -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;
index a08c34a..a15925a 100644 (file)
@@ -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);
index a1fdba9..716b149 100644 (file)
@@ -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;
index 2045125..addfca0 100644 (file)
@@ -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 \
index 7d0be13..8d96291 100644 (file)
@@ -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
index 4b41bee..a6b4f01 100644 (file)
 #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;
 }
 
index 1713692..d0ab5a6 100644 (file)
@@ -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;
index fea8fee..9177e58 100644 (file)
@@ -42,6 +42,7 @@ static GDebugKey keys[] = {
        { "gnupg", GCR_DEBUG_GNUPG },
        { "trust", GCR_DEBUG_TRUST },
        { "import", GCR_DEBUG_IMPORT },
+       { "key", GCR_DEBUG_KEY },
        { 0, }
 };
 
index 20bc35f..5063b77 100644 (file)
@@ -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);
index 1011797..82b24c6 100644 (file)
 
 #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;
 }
index 84cc41f..b48f170 100644 (file)
@@ -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-key-size.c b/gcr/gcr-key-size.c
deleted file mode 100644 (file)
index 1b94dd8..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2010 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 "gcr-key-size.h"
-#include "gcr-oids.h"
-
-#include "egg/egg-asn1x.h"
-#include "egg/egg-asn1-defs.h"
-
-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_key_size_calculate (GNode *subject_public_key)
-{
-       EggBytes *key;
-       guint key_size = 0, 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);
-
-       /* RSA keys are stored in the main subjectPublicKey field */
-       if (oid == GCR_OID_PKIX1_RSA) {
-
-               /* A bit string so we cannot process in place */
-               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 in certificate: %s", g_quark_to_string (oid));
-       }
-
-       return key_size;
-}
diff --git a/gcr/gcr-key-size.h b/gcr/gcr-key-size.h
deleted file mode 100644 (file)
index 8232cd3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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>
- */
-
-#ifndef __GCR_KEY_SIZE_H__
-#define __GCR_KEY_SIZE_H__
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-guint       _gcr_key_size_calculate           (GNode *subject_public_key);
-
-G_END_DECLS
-
-#endif /* __GCR_KEY_RENDERER_H__ */
index e8a5116..7b4ea88 100644 (file)
@@ -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 (file)
index 0000000..fb93b56
--- /dev/null
@@ -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 (file)
index 0000000..bfee315
--- /dev/null
@@ -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 */
index dd522be..e8a01c5 100644 (file)
@@ -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,
index c133c17..5e611f7 100644 (file)
@@ -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 (file)
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 (file)
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 (file)
index 0000000..0e99563
--- /dev/null
@@ -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;
+}
index c5c8b38..b8f5733 100644 (file)
@@ -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