/* -*- 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, see . 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 { GBytes *crt_data; GckAttributes *crt_attrs; GBytes *key_data; GckAttributes *prv_attrs; GBytes *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 (GBytes *data, GcrDataFormat format) { GcrParser *parser; GckAttributes *attrs = NULL; GError *error = NULL; parser = gcr_parser_new (); gcr_parser_format_disable (parser, GCR_FORMAT_ALL); gcr_parser_format_enable (parser, format); g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), &attrs); gcr_parser_parse_bytes (parser, 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 "/gcr/fixtures/%s.crt", fixture->basename); g_file_get_contents (filename, &contents, &length, &error); g_assert_no_error (error); test->crt_data = g_bytes_new_take (contents, length); test->crt_attrs = parse_attributes (test->crt_data, GCR_FORMAT_DER_CERTIFICATE_X509); 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 "/gcr/fixtures/%s.key", fixture->basename); g_file_get_contents (filename, &contents, &length, &error); g_assert_no_error (error); test->key_data = g_bytes_new_take (contents, length); test->prv_attrs = parse_attributes (test->key_data, GCR_FORMAT_ALL); 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 "/gcr/fixtures/%s.spk", fixture->basename); g_file_get_contents (filename, &contents, &length, &error); g_assert_no_error (error); test->spk_data = g_bytes_new_take (contents, length); test->pub_attrs = parse_attributes (test->spk_data, GCR_FORMAT_DER_SUBJECT_PUBLIC_KEY); 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) { g_bytes_unref (test->crt_data); g_bytes_unref (test->key_data); g_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; GBytes *data; info = _gcr_subject_public_key_for_attributes (attrs); g_assert (info != NULL); data = egg_asn1x_encode (info, NULL); egg_assert_cmpbytes (data, ==, g_bytes_get_data (test->spk_data, NULL), g_bytes_get_size (test->spk_data)); g_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 */ g_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); g_object_add_weak_pointer (G_OBJECT (test->module), (gpointer *)&test->module); 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_add_weak_pointer (G_OBJECT (test->session), (gpointer *)&test->session); g_object_unref (slot); } static void teardown_module (TestModule *test, gconstpointer fixture) { CK_RV rv; g_object_unref (test->session); g_assert (test->session == NULL); g_object_unref (test->module); g_assert (test->module == NULL); 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) { GckBuilder builder = GCK_BUILDER_INIT; const gchar *id = "test-id"; gulong handle; setup_attributes (&test->at, fixture); setup_module (&test->mo, NULL); gck_builder_add_all (&builder, test->at.crt_attrs); gck_builder_add_string (&builder, CKA_ID, id); handle = gck_mock_module_add_object (gck_builder_end (&builder)); test->crt_object = gck_object_from_handle (test->mo.session, handle); g_object_add_weak_pointer (G_OBJECT (test->crt_object), (gpointer *)&test->crt_object); gck_builder_add_all (&builder, test->at.pub_attrs); gck_builder_add_string (&builder, CKA_ID, id); handle = gck_mock_module_add_object (gck_builder_end (&builder)); test->pub_object = gck_object_from_handle (test->mo.session, handle); g_object_add_weak_pointer (G_OBJECT (test->pub_object), (gpointer *)&test->pub_object); gck_builder_add_all (&builder, test->at.prv_attrs); gck_builder_add_string (&builder, CKA_ID, id); handle = gck_mock_module_add_object (gck_builder_end (&builder)); test->prv_object = gck_object_from_handle (test->mo.session, handle); g_object_add_weak_pointer (G_OBJECT (test->prv_object), (gpointer *)&test->prv_object); } static void teardown_loading (TestLoading *test, gconstpointer fixture) { g_object_unref (test->crt_object); g_assert (test->crt_object == NULL); g_object_unref (test->prv_object); g_assert (test->prv_object == NULL); g_object_unref (test->pub_object); g_assert (test->pub_object == NULL); teardown_module (&test->mo, NULL); teardown_attributes (&test->at, fixture); } static void perform_load (TestLoading *test, GckObject *object) { GError *error = NULL; GBytes *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, ==, g_bytes_get_data (test->at.spk_data, NULL), g_bytes_get_size (test->at.spk_data)); g_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; GBytes *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, ==, g_bytes_get_data (test->at.spk_data, NULL), g_bytes_get_size (test->at.spk_data)); g_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_cache_init (GckObjectCacheIface *iface); G_DEFINE_TYPE_WITH_CODE (MockObject, mock_object, GCK_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GCK_TYPE_OBJECT_CACHE, mock_object_cache_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_fill (GckObjectCache *object, GckAttributes *attrs) { GckBuilder builder = GCK_BUILDER_INIT; MockObject *self = (MockObject *)object; gck_builder_add_all (&builder, self->attrs); gck_builder_set_all (&builder, attrs); gck_attributes_unref (self->attrs); self->attrs = gck_attributes_ref_sink (gck_builder_end (&builder)); } static void mock_object_cache_init (GckObjectCacheIface *iface) { iface->default_types = NULL; iface->n_default_types = 0; iface->fill = mock_object_fill; } static void perform_load_already (TestLoading *test, GckAttributes *attributes) { const gulong INVALID = 0xFFF00FF; /* invalid handle, should not be used */ GckObject *object; GError *error = NULL; GBytes *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, ==, g_bytes_get_data (test->at.spk_data, NULL), g_bytes_get_size (test->at.spk_data)); g_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) { GckBuilder builder = GCK_BUILDER_INIT; GckAttributes *partial; GckObject *object; GError *error = NULL; GBytes *data; GNode *info; guint i; for (i = 0; i < gck_attributes_count (attributes); i += 2) gck_builder_add_attribute (&builder, gck_attributes_at (attributes, i)); partial = gck_attributes_ref_sink (gck_builder_end (&builder)); 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, ==, g_bytes_get_data (test->at.spk_data, NULL), g_bytes_get_size (test->at.spk_data)); g_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) { GckBuilder builder = GCK_BUILDER_INIT; GckAttributes *attributes; const gulong INVALID = 0xFFF00FF; /* invalid handle, shouldn't be used */ GckObject *object; GError *error = NULL; GNode *info; gck_builder_add_ulong (&builder, CKA_CLASS, CKO_CERTIFICATE); gck_builder_add_ulong (&builder, CKA_CERTIFICATE_TYPE, CKC_X_509); gck_builder_add_string (&builder, CKA_VALUE, "invalid value"); attributes = gck_attributes_ref_sink (gck_builder_end (&builder)); 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 }, { "ec", "ecc-strong", 521 }, }; 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_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; }