From c2c73dfc3ff6c94aa5615c1c8047ed8b862f7b6f Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 18 Nov 2011 13:22:40 +0100 Subject: [PATCH] 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(-) delete mode 100644 gcr/gcr-key-size.c delete mode 100644 gcr/gcr-key-size.h create mode 100644 gcr/gcr-subject-public-key.c create mode 100644 gcr/gcr-subject-public-key.h create mode 100644 gcr/tests/files/client.spk create mode 100644 gcr/tests/files/generic-dsa.spk create mode 100644 gcr/tests/test-subject-public-key.c 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
gcr-fingerprint gcr_fingerprint_from_attributes -gcr_fingerprint_from_certificate_public_key gcr_fingerprint_from_subject_public_key_info
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 -#include /** * 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-key-size.c b/gcr/gcr-key-size.c deleted file mode 100644 index 1b94dd8..0000000 --- a/gcr/gcr-key-size.c +++ /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 index 8232cd3..0000000 --- a/gcr/gcr-key-size.h +++ /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 - */ - -#ifndef __GCR_KEY_SIZE_H__ -#define __GCR_KEY_SIZE_H__ - -#include - -G_BEGIN_DECLS - -guint _gcr_key_size_calculate (GNode *subject_public_key); - -G_END_DECLS - -#endif /* __GCR_KEY_RENDERER_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 + */ +#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 + +#include + +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 + */ + +#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef GCR_SUBJECT_PUBLIC_KEY_H +#define GCR_SUBJECT_PUBLIC_KEY_H + +#include + +#include + +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 0000000000000000000000000000000000000000..09ab6b930efe8557c67e187da61ba01727489789 GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>l!5}mXPDM~7 zyqd>0i3z83(=`%xS!-S$~eFU0hT4Vt+7?%(hj8j=kYZUO#e5KS1Bf+Z)M+(kX#l*gu@V#5^pg zN3uaz0u=zwLn%kC5ZPL2e~@?sw<8AxG{l(#fq?*#ZjTZ6SRPwb>m!Zh@C7tpzpAZc zGr(N;ezLH;vWd5iRCtc#vIt0r!h}raE3P zyw7`Kn7Jg0QPT*|@8?;Fl4oHiIXuROqyi-F62`Fh$y=o7qc9i1Bjt-#?xD+@zOdGf lpYuuUCp&v081RU=|9c{(TLhty8~e#q0ZY;B8~@={Yri!4&36C* literal 0 HcmV?d00001 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 +*/ + +#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 + +#include + +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 -- 2.7.4