[gnome-keyring/trust-store] [gcr] Add GcrCertificateChain
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring/trust-store] [gcr] Add GcrCertificateChain
- Date: Fri, 10 Dec 2010 22:27:45 +0000 (UTC)
commit 2d97fa8b25bd328c5f678412e2804c0d6bbeb071
Author: Stef Walter <stefw collabora co uk>
Date: Fri Dec 10 21:08:55 2010 +0000
[gcr] Add GcrCertificateChain
Represents a certificate chain, and has an operation which builds
the chain into a complete chain and identifies the type of chain.
docs/reference/gcr/gcr-docs.sgml | 1 +
docs/reference/gcr/gcr-sections.txt | 28 ++
docs/reference/gcr/gcr.types | 3 +-
gcr/Makefile.am | 2 +
gcr/gcr-certificate-chain.c | 735 +++++++++++++++++++++++++++++
gcr/gcr-certificate-chain.h | 113 +++++
gcr/gcr.h | 1 +
gcr/tests/Makefile.am | 1 +
gcr/tests/test-certificate-chain.c | 580 +++++++++++++++++++++++
gcr/tests/test-data/collabora-ca.cer | Bin 0 -> 1536 bytes
gcr/tests/test-data/dhansak-collabora.cer | Bin 0 -> 1200 bytes
11 files changed, 1463 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gcr/gcr-docs.sgml b/docs/reference/gcr/gcr-docs.sgml
index 767fc97..d7e826c 100644
--- a/docs/reference/gcr/gcr-docs.sgml
+++ b/docs/reference/gcr/gcr-docs.sgml
@@ -16,6 +16,7 @@
<xi:include href="xml/gcr-certificate.xml"/>
<xi:include href="xml/gcr-simple-certificate.xml"/>
<xi:include href="xml/gcr-pkcs11-certificate.xml"/>
+ <xi:include href="xml/gcr-certificate-chain.xml"/>
</part>
<part id="storage">
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 299ce95..1d714d1 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -178,4 +178,32 @@ gcr_pkcs11_get_trust_lookup_modules
gcr_pkcs11_get_trust_store_slot
gcr_pkcs11_get_trust_store_uri
gcr_pkcs11_set_trust_store_uri
+</SECTION>
+
+<SECTION>
+<FILE>gcr-certificate-chain</FILE>
+GcrCertificateChain
+GcrCertificateChainType
+gcr_certificate_chain_new
+gcr_certificate_chain_add
+gcr_certificate_chain_get_certificate
+gcr_certificate_chain_get_chain_type
+gcr_certificate_chain_get_anchor
+gcr_certificate_chain_get_endpoint
+gcr_certificate_chain_get_length
+GcrCertificateChainFlags
+gcr_certificate_chain_build
+gcr_certificate_chain_build_async
+gcr_certificate_chain_build_finish
+<SUBSECTION Standard>
+gcr_certificate_chain_get_type
+GCR_CERTIFICATE_CHAIN
+GCR_TYPE_CERTIFICATE_CHAIN
+GCR_CERTIFICATE_CHAIN_CLASS
+GCR_CERTIFICATE_CHAIN_GET_CLASS
+GCR_IS_CERTIFICATE_CHAIN
+GCR_IS_CERTIFICATE_CHAIN_CLASS
+GcrCertificateChainClass
+GcrCertificateChainPrivate
+
</SECTION>
\ No newline at end of file
diff --git a/docs/reference/gcr/gcr.types b/docs/reference/gcr/gcr.types
index dc87a0a..a54679c 100644
--- a/docs/reference/gcr/gcr.types
+++ b/docs/reference/gcr/gcr.types
@@ -2,4 +2,5 @@ gcr_parser_get_type
gcr_certificate_get_type
gcr_importer_get_type
gcr_simple_certificate_get_type
-gcr_pkcs11_certificate_get_type
\ No newline at end of file
+gcr_pkcs11_certificate_get_type
+gcr_certificate_chain_get_type
\ No newline at end of file
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 015d5e8..aad078a 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -16,6 +16,7 @@ incdir = $(includedir)/gcr GCR_VERSION_SUFFIX@/gcr
inc_HEADERS = \
gcr.h \
gcr-certificate.h \
+ gcr-certificate-chain.h \
gcr-certificate-renderer.h \
gcr-certificate-widget.h \
gcr-key-renderer.h \
@@ -51,6 +52,7 @@ lib_LTLIBRARIES = libgcr GCR_VERSION_SUFFIX@.la
libgcr GCR_VERSION_SUFFIX@_la_SOURCES = \
gcr-certificate.c gcr-certificate.h \
+ gcr-certificate-chain.c gcr-certificate-chain.h \
gcr-certificate-renderer.c gcr-certificate-renderer.h \
gcr-certificate-widget.c gcr-certificate-widget.h \
gcr-display-view.c gcr-display-view.h \
diff --git a/gcr/gcr-certificate-chain.c b/gcr/gcr-certificate-chain.c
new file mode 100644
index 0000000..89fc921
--- /dev/null
+++ b/gcr/gcr-certificate-chain.c
@@ -0,0 +1,735 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 Collabora Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-certificate-chain.h"
+
+#include "gcr-certificate.h"
+#include "gcr-pkcs11-certificate.h"
+#include "gcr-simple-certificate.h"
+
+#include "gcr-trust.h"
+
+/**
+ * SECTION:gcr-certificate-chain
+ * @title: GcrCertificateChain
+ * @short_description: A certificate chain
+ *
+ * #GcrCertificateChain represents a chain of certificates, normally used to
+ * validate the trust in a certificate. An X.509 certificate chain has one
+ * endpoint certificate (the one for which trust is being verified) and then
+ * in turn the certificate that issued each previous certificate in the chain.
+ *
+ * This functionality is for building of certificate chains not for validating
+ * them. Use your favorite crypto library to validate trust in a certificate
+ * chain once its built.
+ *
+ * The order of certificates in the chain should be first the endpoint
+ * certificates and then the signing certificates.
+ *
+ * Create a new certificate chain with gcr_certificate_chain_new() and then
+ * add the certificates with gcr_certificate_chain_add().
+ *
+ * You can then use gcr_certificate_chain_build() to build the remainder of
+ * the chain. This will lookup missing certificates in PKCS\#11 modules and
+ * also check that each certificate in the chain is the signer of the previous
+ * one. If a trust anchor, pinned certificate, or self-signed certificate is
+ * found, then the chain is considered built. Any extra certificates are
+ * removed from the chain.
+ *
+ * Once the certificate chain has been built, you can access its type
+ * through gcr_certificate_chain_get_chain_type(). The type signifies whether
+ * the chain is anchored on a trust root, self-signed, incomplete etc. See
+ * #GcrCertificateChainType for information on the various types.
+ *
+ * It's important to understand that the building of a certificate chain is
+ * merely the first step towards verifying trust in a certificate.
+ */
+
+/**
+ * GcrCertificateChainType:
+ * @GCR_CERTIFICATE_CHAIN_UNKNOWN: The certificate chain's type is unknown.
+ * When a chain is not yet built it has this type. If a chain is modified after
+ * being built, it has this type.
+ * @GCR_CERTIFICATE_CHAIN_INCOMPLETE: A full chain could not be loaded. The
+ * chain does not end with a self-signed certificate, a trusted anchor, or a
+ * pinned certificate.
+ * @GCR_CERTIFICATE_CHAIN_SELFSIGNED: The chain ends with a self-signed
+ * certificate. No trust anchor was found.
+ * @GCR_CERTIFICATE_CHAIN_ANCHORED: The chain ends with an anchored
+ * certificate. The anchored certificate is not necessarily self-signed.
+ * @GCR_CERTIFICATE_CHAIN_PINNED: The chain represents a pinned certificate. A
+ * pinned certificate is an exception which trusts a given certificate
+ * explicitly for a purpose and communication with a certain peer.
+ *
+ * The type of a built certificate chain. Will be set to
+ * %GCR_CERTIFICATE_CHAIN_UNKNOWN for certificate chains that have not been
+ * built.
+ */
+
+/**
+ * GcrCertificateChainFlags:
+ * @GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS: If this flag is specified then no
+ * lookups for anchors or pinned certificates are done, and the resulting chain
+ * will be neither anchored or pinned. Additionally no missing certificate
+ * authorities are looked up in PKCS\#11.
+ *
+ * Flags to be used with the gcr_certificate_chain_build() operation.
+ */
+
+enum {
+ PROP_0,
+ PROP_TYPE,
+ PROP_LENGTH,
+};
+
+struct _GcrCertificateChainPrivate {
+ GPtrArray *certificates;
+ GcrCertificateChainType type;
+
+ /* Used in build operation */
+ gchar *purpose;
+ gchar *peer;
+ guint flags;
+};
+
+static GQuark Q_ORIGINAL_CERT = 0;
+static GQuark Q_OPERATION_DATA = 0;
+
+G_DEFINE_TYPE (GcrCertificateChain, gcr_certificate_chain, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+free_chain_private (gpointer data)
+{
+ GcrCertificateChainPrivate *pv = data;
+ g_ptr_array_unref (pv->certificates);
+ g_free (pv->purpose);
+ g_free (pv->peer);
+ g_slice_free (GcrCertificateChainPrivate, pv);
+}
+
+static GcrCertificateChainPrivate*
+new_chain_private (void)
+{
+ GcrCertificateChainPrivate *pv = g_slice_new0 (GcrCertificateChainPrivate);
+ pv->certificates = g_ptr_array_new_with_free_func (g_object_unref);
+ return pv;
+}
+
+static GcrCertificateChainPrivate*
+prep_chain_private (GcrCertificateChainPrivate *orig, const gchar *purpose,
+ const gchar *peer, guint flags)
+{
+ GcrCertificateChainPrivate *pv;
+ GcrCertificate *certificate;
+ guint i;
+
+ g_assert (orig);
+ g_assert (purpose);
+
+ pv = new_chain_private ();
+ for (i = 0; i < orig->certificates->len; ++i) {
+ certificate = g_ptr_array_index (orig->certificates, i);
+ g_ptr_array_add (pv->certificates, g_object_ref (certificate));
+ }
+
+ pv->type = orig->type;
+ pv->purpose = g_strdup (purpose);
+ pv->peer = g_strdup (peer);
+ pv->flags = flags;
+ return pv;
+}
+
+static GcrCertificateChainPrivate*
+prep_chain_private_thread_safe (GcrCertificateChainPrivate *orig, const gchar *purpose,
+ const gchar *peer, guint flags)
+{
+ GcrCertificateChainPrivate *pv;
+ GcrCertificate *certificate, *safe;
+ gconstpointer der;
+ gsize n_der;
+ guint i;
+
+ g_assert (orig);
+
+ pv = prep_chain_private (orig, purpose, peer, flags);
+
+ for (i = 0; i < pv->certificates->len; ++i) {
+ certificate = g_ptr_array_index (pv->certificates, i);
+
+ /* We regard these types as thread safe */
+ if (GCR_IS_SIMPLE_CERTIFICATE (certificate) ||
+ GCR_IS_PKCS11_CERTIFICATE (certificate)) {
+ safe = g_object_ref (certificate);
+
+ /* Otherwise copy the certificate data */
+ } else {
+ der = gcr_certificate_get_der_data (certificate, &n_der);
+ g_return_val_if_fail (der, NULL);
+ safe = gcr_simple_certificate_new (der, n_der);
+ }
+
+ /* Always set the original certificate onto the safe one */
+ g_object_set_qdata_full (G_OBJECT (safe), Q_ORIGINAL_CERT,
+ g_object_ref (certificate), g_object_unref);
+
+ g_ptr_array_index (pv->certificates, i) = safe;
+ g_object_unref (certificate);
+ }
+
+ return pv;
+}
+
+static GcrCertificateChainPrivate*
+cleanup_chain_private (GcrCertificateChainPrivate *pv)
+{
+ GcrCertificate *certificate, *orig;
+ guint i;
+
+ for (i = 0; i < pv->certificates->len; ++i) {
+ certificate = g_ptr_array_index (pv->certificates, i);
+
+ /* If there's an original certificate set, then replace it back */
+ orig = g_object_get_qdata (G_OBJECT (certificate), Q_ORIGINAL_CERT);
+ if (orig != NULL) {
+ g_ptr_array_index (pv->certificates, i) = g_object_ref (orig);
+ g_object_unref (certificate);
+ }
+ }
+
+ return pv;
+}
+
+static gboolean
+perform_build_chain (GcrCertificateChainPrivate *pv, GCancellable *cancellable,
+ GError **rerror)
+{
+ GError *error = NULL;
+ GcrCertificate *certificate;
+ gboolean lookups;
+ gboolean ret;
+ guint length;
+
+ g_assert (pv);
+ g_assert (pv->certificates);
+
+ pv->type = GCR_CERTIFICATE_CHAIN_UNKNOWN;
+ lookups = !((pv->flags & GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS) == GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS);
+
+ /* This chain is built */
+ if (!pv->certificates->len)
+ return TRUE;
+
+ /* First check for pinned certificates */
+ certificate = g_ptr_array_index (pv->certificates, 0);
+ if (lookups && pv->peer) {
+ ret = gcr_trust_is_certificate_exception (certificate, pv->purpose,
+ pv->peer, cancellable, &error);
+ if (!ret && error) {
+ g_propagate_error (rerror, error);
+ return FALSE;
+ }
+
+ /*
+ * This is a pinned certificate and the rest of the chain
+ * is irrelevant, so truncate chain and consider built.
+ */
+ if (ret) {
+ g_ptr_array_set_size (pv->certificates, 1);
+ pv->type = GCR_CERTIFICATE_CHAIN_PINNED;
+ return TRUE;
+ }
+ }
+
+ length = 1;
+
+ /* The first certificate is always unconditionally in the chain */
+ while (pv->type == GCR_CERTIFICATE_CHAIN_UNKNOWN) {
+
+ /* Stop the chain if previous was self-signed */
+ if (gcr_certificate_is_issuer (certificate, certificate)) {
+ pv->type = GCR_CERTIFICATE_CHAIN_SELFSIGNED;
+ break;
+ }
+
+ /* Try the next certificate in the chain */
+ if (length < pv->certificates->len) {
+ certificate = g_ptr_array_index (pv->certificates, length);
+
+ /* No more in chain, try to lookup */
+ } else if (lookups) {
+ certificate = gcr_pkcs11_certificate_lookup_issuer (certificate,
+ cancellable, &error);
+ if (error != NULL) {
+ g_propagate_error (rerror, error);
+ return FALSE;
+ } else if (certificate) {
+ g_ptr_array_add (pv->certificates, certificate);
+ }
+
+ /* No more in chain, and can't lookup */
+ } else {
+ certificate = NULL;
+ }
+
+ /* Stop the chain if nothing found */
+ if (certificate == NULL) {
+ pv->type = GCR_CERTIFICATE_CHAIN_INCOMPLETE;
+ break;
+ }
+
+ ++length;
+
+ /* See if this certificate is an anchor */
+ if (lookups) {
+ ret = gcr_trust_is_certificate_anchor (certificate, pv->purpose,
+ cancellable, &error);
+
+ if (!ret && error) {
+ g_propagate_error (rerror, error);
+ return FALSE;
+
+ /* Stop the chain at the first anchor */
+ } else if (ret) {
+ pv->type = GCR_CERTIFICATE_CHAIN_ANCHORED;
+ break;
+ }
+ }
+ }
+
+ /* Truncate to the appropriate length */
+ g_assert (length <= pv->certificates->len);
+ g_ptr_array_set_size (pv->certificates, length);
+ return TRUE;
+}
+
+static void
+thread_build_chain (GSimpleAsyncResult *result, GObject *object,
+ GCancellable *cancellable)
+{
+ GcrCertificateChainPrivate *pv;
+ GError *error = NULL;
+
+ pv = g_object_get_qdata (G_OBJECT (result), Q_OPERATION_DATA);
+ g_assert (pv);
+
+ if (!perform_build_chain (pv, cancellable, &error)) {
+ g_simple_async_result_set_from_error (result, error);
+ g_clear_error (&error);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gcr_certificate_chain_init (GcrCertificateChain *self)
+{
+ self->pv = new_chain_private ();
+}
+
+static void
+gcr_certificate_chain_dispose (GObject *obj)
+{
+ GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
+
+ g_ptr_array_set_size (self->pv->certificates, 0);
+ self->pv->type = GCR_CERTIFICATE_CHAIN_UNKNOWN;
+
+ G_OBJECT_CLASS (gcr_certificate_chain_parent_class)->dispose (obj);
+}
+
+static void
+gcr_certificate_chain_finalize (GObject *obj)
+{
+ GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
+
+ free_chain_private (self->pv);
+ self->pv = NULL;
+
+ G_OBJECT_CLASS (gcr_certificate_chain_parent_class)->finalize (obj);
+}
+
+static void
+gcr_certificate_chain_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
+
+ switch (prop_id) {
+ case PROP_TYPE:
+ g_value_set_uint (value, gcr_certificate_chain_get_chain_type (self));
+ break;
+ case PROP_LENGTH:
+ g_value_set_uint (value, gcr_certificate_chain_get_length (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_chain_class_init (GcrCertificateChainClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gcr_certificate_chain_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = gcr_certificate_chain_dispose;
+ gobject_class->finalize = gcr_certificate_chain_finalize;
+ gobject_class->get_property = gcr_certificate_chain_get_property;
+
+ /**
+ * GcrCertificateChain:type:
+ *
+ * The certificate chain type. See #GcrCertificateChainType
+ */
+ g_object_class_install_property (gobject_class, PROP_TYPE,
+ g_param_spec_uint ("type", "Type", "Type of certificate chain",
+ 0, _GCR_CERTIFICATE_CHAIN_TYPE_MAX,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN, G_PARAM_READABLE));
+
+ /**
+ * GcrCertificateChain:length:
+ *
+ * The length of the certificate chain.
+ */
+ g_object_class_install_property (gobject_class, PROP_LENGTH,
+ g_param_spec_uint ("length", "Length", "Length of certificate chain",
+ 0, G_MAXUINT, 0, G_PARAM_READABLE));
+
+ Q_ORIGINAL_CERT = g_quark_from_static_string ("gcr-certificate-chain-original-cert");
+ Q_OPERATION_DATA = g_quark_from_static_string ("gcr-certificate-chain-operation-data");
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+/**
+ * gcr_certificate_chain_new:
+ *
+ * Create a new #GcrCertificateChain.
+ *
+ * Returns: A newly allocated #GcrCertificateChain
+ */
+GcrCertificateChain*
+gcr_certificate_chain_new (void)
+{
+ return g_object_new (GCR_TYPE_CERTIFICATE_CHAIN, NULL);
+}
+
+/**
+ * gcr_certificate_chain_add:
+ * @self: the #GcrCertificateChain
+ * @certificate: a #GcrCertificate to add to the chain
+ *
+ * Add @certificate to the chain. The order of certificates in the chain are
+ * important. The first certificate should be the endpoint certificate, and
+ * then come the signers (certificate authorities) each in turn. If a root
+ * certificate authority is present, it should come last.
+ *
+ * Adding a certificate an already built chain (see
+ * gcr_certificate_chain_build()) resets the type of the certificate chain
+ * to %GCR_CERTIFICATE_CHAIN_UNKNOWN
+ */
+void
+gcr_certificate_chain_add (GcrCertificateChain *self, GcrCertificate *certificate)
+{
+ g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
+ g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
+ g_ptr_array_add (self->pv->certificates, g_object_ref (certificate));
+ self->pv->type = GCR_CERTIFICATE_CHAIN_UNKNOWN;
+ g_object_notify (G_OBJECT (self), "type");
+ g_object_notify (G_OBJECT (self), "length");
+}
+
+/**
+ * gcr_certificate_chain_get_chain_type:
+ * @self: the #GcrCertificateChain
+ *
+ * Get the type of a certificate chain. If the certificate chain has not
+ * been built, then the type will be %GCR_CERTIFICATE_CHAIN_UNKNOWN.
+ *
+ * A type of %GCR_CERTIFICATE_CHAIN_ANCHORED does not mean that the
+ * certificate chain has been verified, but merely that an anchor has been
+ * found.
+ *
+ * Returns: the type of the certificate chain.
+ */
+GcrCertificateChainType
+gcr_certificate_chain_get_chain_type (GcrCertificateChain *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), GCR_CERTIFICATE_CHAIN_UNKNOWN);
+ return self->pv->type;
+}
+
+/**
+ * gcr_certificate_chain_get_anchor:
+ * @self: the #GcrCertificateChain
+ *
+ * If the certificate chain has been built and is of type
+ * %GCR_CERTIFICATE_CHAIN_ANCHORED, then this will return the anchor
+ * certificate that was found. This is not necessarily a root certificate
+ * authority. If an intermediate certificate authority in the chain was
+ * found to be anchored, then that certificate will be returned.
+ *
+ * If an anchor is returned it does not mean that the certificate chain has
+ * been verified, but merely that an anchor has been found.
+ *
+ * Returns: the anchor certificate, or NULL if not anchored.
+ */
+GcrCertificate*
+gcr_certificate_chain_get_anchor (GcrCertificateChain *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
+ if (self->pv->type != GCR_CERTIFICATE_CHAIN_ANCHORED)
+ return NULL;
+ g_assert (self->pv->certificates->len > 0);
+ return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates,
+ self->pv->certificates->len - 1));
+}
+
+/**
+ * gcr_certificate_chain_get_endpoint:
+ * @self: the #GcrCertificateChain
+ *
+ * Get the endpoint certificate in the chain. This is always the first
+ * certificate in the chain. The endpoint certificate cannot be anchored.
+ *
+ * Returns: the endpoint certificate, or NULL if the chain is empty.
+ */
+GcrCertificate*
+gcr_certificate_chain_get_endpoint (GcrCertificateChain *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
+ if (!self->pv->certificates->len)
+ return NULL;
+ return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates, 0));
+}
+
+/**
+ * gcr_certificate_chain_get_length:
+ * @self: the #GcrCertificateChain
+ *
+ * Get the length of the certificate chain.
+ *
+ * Returns: the length of the certificate chain
+ */
+guint
+gcr_certificate_chain_get_length (GcrCertificateChain *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), 0);
+ return self->pv->certificates->len;
+}
+
+/**
+ * gcr_certificate_chain_get_certificate:
+ * @self: the #GcrCertificateChain
+ * @index: index of the certificate to get
+ *
+ * Get a certificate in the chain. It is an error to call this function
+ * with an invalid index.
+ *
+ * Returns: the certificate
+ */
+GcrCertificate*
+gcr_certificate_chain_get_certificate (GcrCertificateChain *self, guint index)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
+ g_return_val_if_fail (index < self->pv->certificates->len, NULL);
+ return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates, index));
+}
+
+/**
+ * gcr_certificate_chain_build:
+ * @self: the #GcrCertificateChain
+ * @purpose: the purpose the certificate chain will be used for
+ * @peer: the peer the certificate chain will be used with, or NULL
+ * @flags: chain completion flags
+ * @cancellable: a #GCancellable or %NULL
+ * @error: a #GError or %NULL
+ *
+ * Complete a certificate chain. Once a certificate chain has been built
+ * its type can be examined.
+ *
+ * This operation will lookup missing certificates in PKCS\#11
+ * modules and also that each certificate in the chain is the signer of the
+ * previous one. If a trust anchor, pinned certificate, or self-signed certificate
+ * is found, then the chain is considered built. Any extra certificates are
+ * removed from the chain.
+ *
+ * It's important to understand that building of a certificate chain does not
+ * constitute verifying that chain. This is merely the first step towards
+ * trust verification.
+ *
+ * The @purpose is a string like #GCR_PURPOSE_CLIENT_AUTH and is the purpose
+ * for which the certificate chain will be used. Trust anchors are looked up
+ * for this purpose. This argument is required.
+ *
+ * The @peer is usually the host name of the peer whith which this certificate
+ * chain is being used. It is used to look up pinned certificates that have
+ * been stored for this peer. If %NULL then no pinned certificates will
+ * be considered.
+ *
+ * If the #GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS flag is specified then no
+ * lookups for anchors or pinned certificates are done, and the resulting chain
+ * will be neither anchored or pinned. Additionally no missing certificate
+ * authorities are looked up in PKCS\#11
+ *
+ * This call will block, see gcr_certificate_chain_build_async() for the
+ * asynchronous version.
+ *
+ * Returns: whether the operation completed successfully
+ */
+gboolean
+gcr_certificate_chain_build (GcrCertificateChain *self, const gchar *purpose,
+ const gchar *peer, guint flags,
+ GCancellable *cancellable, GError **error)
+{
+ GcrCertificateChainPrivate *pv;
+ gboolean ret;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), FALSE);
+ g_return_val_if_fail (purpose, FALSE);
+
+ pv = prep_chain_private (self->pv, purpose, peer, flags);
+
+ ret = perform_build_chain (pv, cancellable, error);
+
+ if (ret) {
+ free_chain_private (self->pv);
+ self->pv = cleanup_chain_private (pv);
+ g_object_notify (G_OBJECT (self), "type");
+ g_object_notify (G_OBJECT (self), "length");
+ }
+
+ return ret;
+}
+
+/**
+ * gcr_certificate_chain_build_async:
+ * @self: the #GcrCertificateChain
+ * @purpose: the purpose the certificate chain will be used for
+ * @peer: the peer the certificate chain will be used with, or NULL
+ * @flags: chain completion flags
+ * @cancellable: a #GCancellable or %NULL
+ * @callback: this will be called when the operation completes.
+ * @user_data: data to pass to the callback
+ *
+ * Complete a certificate chain. Once a certificate chain has been built
+ * its type can be examined.
+ *
+ * This will lookup missing certificates in PKCS\#11
+ * modules and also that each certificate in the chain is the signer of the
+ * previous one. If a trust anchor, pinned certificate, or self-signed certificate
+ * is found, then the chain is considered built. Any extra certificates are
+ * removed from the chain.
+ *
+ * It's important to understand that building of a certificate chain does not
+ * constitute verifying that chain. This is merely the first step towards
+ * trust verification.
+ *
+ * The @purpose is a string like #GCR_PURPOSE_CLIENT_AUTH and is the purpose
+ * for which the certificate chain will be used. Trust anchors are looked up
+ * for this purpose. This argument is required.
+ *
+ * The @peer is usually the host name of the peer whith which this certificate
+ * chain is being used. It is used to look up pinned certificates that have
+ * been stored for this peer. If %NULL then no pinned certificates will
+ * be considered.
+ *
+ * If the #GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS flag is specified then no
+ * lookups for anchors or pinned certificates are done, and the resulting chain
+ * will be neither anchored or pinned. Additionally no missing certificate
+ * authorities are looked up in PKCS\#11
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * gcr_certificate_chain_build_finish() to get the result of the operation.
+ */
+void
+gcr_certificate_chain_build_async (GcrCertificateChain *self, const gchar *purpose,
+ const gchar *peer, guint flags,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GcrCertificateChainPrivate *pv;
+ GSimpleAsyncResult *result;
+
+ g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
+
+ g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
+ g_return_if_fail (purpose);
+
+ pv = prep_chain_private_thread_safe (self->pv, purpose, peer, flags);
+
+ result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+ gcr_certificate_chain_build_async);
+ g_object_set_qdata_full (G_OBJECT (result), Q_OPERATION_DATA, pv, free_chain_private);
+
+ g_simple_async_result_run_in_thread (result, thread_build_chain,
+ G_PRIORITY_DEFAULT, cancellable);
+
+}
+
+/**
+ * gcr_pkcs11_certificate_lookup_issuer_finish:
+ * @result: the #GAsyncResult passed to the callback
+ * @error: a #GError, or NULL
+ *
+ * Finishes an asynchronous operation started by
+ * gcr_certificate_chain_build_async().
+ *
+ * Returns: whether the operation succeeded
+ */
+gboolean
+gcr_certificate_chain_build_finish (GcrCertificateChain *self, GAsyncResult *result,
+ GError **error)
+{
+ GcrCertificateChainPrivate *pv;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+ gcr_certificate_chain_build_async), FALSE);
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+
+ pv = g_object_steal_qdata (G_OBJECT (result), Q_OPERATION_DATA);
+ g_return_val_if_fail (pv, FALSE);
+
+ free_chain_private (self->pv);
+ self->pv = cleanup_chain_private (pv);
+
+ g_object_notify (G_OBJECT (self), "type");
+ g_object_notify (G_OBJECT (self), "length");
+ return TRUE;
+}
diff --git a/gcr/gcr-certificate-chain.h b/gcr/gcr-certificate-chain.h
new file mode 100644
index 0000000..a1ed212
--- /dev/null
+++ b/gcr/gcr-certificate-chain.h
@@ -0,0 +1,113 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 Collabora Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#ifndef __GCR_CERTIFICATE_CHAIN_H__
+#define __GCR_CERTIFICATE_CHAIN_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gcr-certificate.h"
+#include "gcr-types.h"
+
+G_BEGIN_DECLS
+
+typedef enum _GcrCertificateChainType {
+ GCR_CERTIFICATE_CHAIN_UNKNOWN,
+ GCR_CERTIFICATE_CHAIN_INCOMPLETE,
+ GCR_CERTIFICATE_CHAIN_SELFSIGNED,
+ GCR_CERTIFICATE_CHAIN_ANCHORED,
+ GCR_CERTIFICATE_CHAIN_PINNED,
+
+ /*< private >*/
+ _GCR_CERTIFICATE_CHAIN_TYPE_MAX
+} GcrCertificateChainType;
+
+typedef enum _GcrCertificateChainFlags {
+ GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS = 1 << 0,
+} GcrCertificateChainFlags;
+
+#define GCR_TYPE_CERTIFICATE_CHAIN (gcr_certificate_chain_get_type ())
+#define GCR_CERTIFICATE_CHAIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_CHAIN, GcrCertificateChain))
+#define GCR_CERTIFICATE_CHAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_CHAIN, GcrCertificateChainClass))
+#define GCR_IS_CERTIFICATE_CHAIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_CHAIN))
+#define GCR_IS_CERTIFICATE_CHAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_CHAIN))
+#define GCR_CERTIFICATE_CHAIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_CHAIN, GcrCertificateChainClass))
+
+typedef struct _GcrCertificateChain GcrCertificateChain;
+typedef struct _GcrCertificateChainClass GcrCertificateChainClass;
+typedef struct _GcrCertificateChainPrivate GcrCertificateChainPrivate;
+
+struct _GcrCertificateChain {
+ GObject parent;
+ GcrCertificateChainPrivate *pv;
+};
+
+struct _GcrCertificateChainClass {
+ GObjectClass parent_class;
+};
+
+GType gcr_certificate_chain_get_type (void);
+
+GcrCertificateChain* gcr_certificate_chain_new (void);
+
+void gcr_certificate_chain_add (GcrCertificateChain *self,
+ GcrCertificate *certificate);
+
+GcrCertificateChainType gcr_certificate_chain_get_chain_type (GcrCertificateChain *self);
+
+GcrCertificate* gcr_certificate_chain_get_anchor (GcrCertificateChain *self);
+
+GcrCertificate* gcr_certificate_chain_get_endpoint (GcrCertificateChain *self);
+
+guint gcr_certificate_chain_get_length (GcrCertificateChain *self);
+
+GcrCertificate* gcr_certificate_chain_get_certificate (GcrCertificateChain *self,
+ guint index);
+
+gboolean gcr_certificate_chain_build (GcrCertificateChain *self,
+ const gchar *purpose,
+ const gchar *peer,
+ guint flags,
+ GCancellable *cancellable,
+ GError **error);
+
+void gcr_certificate_chain_build_async (GcrCertificateChain *self,
+ const gchar *purpose,
+ const gchar *peer,
+ guint flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean gcr_certificate_chain_build_finish (GcrCertificateChain *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GCR_CERTIFICATE_CHAIN_H__ */
diff --git a/gcr/gcr.h b/gcr/gcr.h
index 84f22c9..30e3649 100644
--- a/gcr/gcr.h
+++ b/gcr/gcr.h
@@ -35,6 +35,7 @@
#include "gcr-types.h"
#include "gcr-certificate.h"
+#include "gcr-certificate-chain.h"
#include "gcr-certificate-renderer.h"
#include "gcr-certificate-widget.h"
#include "gcr-key-renderer.h"
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index ba06b60..3454af5 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -2,6 +2,7 @@
# Test files should be listed in order they need to run
TESTING_FILES = \
test-certificate.c \
+ test-certificate-chain.c \
test-simple-certificate.c \
test-pkcs11-certificate.c \
test-trust.c \
diff --git a/gcr/tests/test-certificate-chain.c b/gcr/tests/test-certificate-chain.c
new file mode 100644
index 0000000..7aeaca6
--- /dev/null
+++ b/gcr/tests/test-certificate-chain.c
@@ -0,0 +1,580 @@
+
+#include "config.h"
+#include "test-suite.h"
+
+#include "egg/egg-asn1x.h"
+#include "egg/egg-asn1-defs.h"
+
+#include "gcr/gcr.h"
+
+#include "gck/gck-mock.h"
+#include "gck/gck-test.h"
+
+#include <glib.h>
+
+#include <string.h>
+
+/* ---------------------------------------------------------------------------
+ * A Mock certificate that checks that it's always called on the
+ * same thread. A GcrCertificate implemented on top of a non-thread-safe
+ * crypto library would require this behavior.
+ */
+
+GType mock_certificate_get_type (void);
+
+#define MOCK_CERTIFICATE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), mock_certificate_get_type (), MockCertificate))
+
+typedef struct _MockCertificate {
+ GObject parent;
+ GThread *created_on;
+ gpointer data;
+ gsize n_data;
+} MockCertificate;
+
+typedef struct _MockCertificateClass {
+ GObjectClass parent_class;
+} MockCertificateClass;
+
+static void mock_certificate_iface (GcrCertificateIface *iface);
+G_DEFINE_TYPE_WITH_CODE (MockCertificate, mock_certificate, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GCR_TYPE_CERTIFICATE, mock_certificate_iface));
+
+static void
+mock_certificate_init (MockCertificate *self)
+{
+ self->created_on = g_thread_self ();
+}
+
+static void
+mock_certificate_finalize (GObject *obj)
+{
+ MockCertificate *self = MOCK_CERTIFICATE (obj);
+ g_assert (self->created_on == g_thread_self ());
+ g_free (self->data);
+ G_OBJECT_CLASS (mock_certificate_parent_class)->finalize (obj);
+}
+
+static void
+mock_certificate_class_init (MockCertificateClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mock_certificate_finalize;
+}
+
+static gconstpointer
+mock_certificate_real_get_der_data (GcrCertificate *base, gsize *n_data)
+{
+ MockCertificate *self = MOCK_CERTIFICATE (base);
+ g_assert (self->created_on == g_thread_self ());
+ *n_data = self->n_data;
+ return self->data;
+}
+
+static void
+mock_certificate_iface (GcrCertificateIface *iface)
+{
+ iface->get_der_data = (gpointer)mock_certificate_real_get_der_data;
+}
+
+static GcrCertificate*
+mock_certificate_new (gconstpointer data, gsize n_data)
+{
+ MockCertificate *self = g_object_new (mock_certificate_get_type (), NULL);
+ self->data = g_memdup (data, n_data);
+ self->n_data = n_data;
+ g_assert (self->created_on == g_thread_self ());
+ return GCR_CERTIFICATE (self);
+}
+
+/* ----------------------------------------------------------------------------
+ * TESTS
+ */
+
+static GcrCertificate *cert_self = NULL;
+static GcrCertificate *cert_ca = NULL;
+static GcrCertificate *cert_signed = NULL;
+static CK_FUNCTION_LIST funcs;
+
+TESTING_SETUP (certificate_chain)
+{
+ GList *modules = NULL;
+ CK_FUNCTION_LIST_PTR f;
+ guchar *contents;
+ gsize n_contents;
+ CK_RV rv;
+ GckModule *module;
+
+ rv = gck_mock_C_GetFunctionList (&f);
+ gck_assert_cmprv (rv, ==, CKR_OK);
+ memcpy (&funcs, f, sizeof (funcs));
+
+ /* Open a session */
+ rv = (funcs.C_Initialize) (NULL);
+ gck_assert_cmprv (rv, ==, CKR_OK);
+
+ g_assert (!modules);
+ module = gck_module_new (&funcs, 0);
+ modules = g_list_prepend (modules, module);
+ gcr_pkcs11_set_modules (modules);
+ gcr_pkcs11_set_trust_store_uri (GCK_MOCK_SLOT_ONE_URI);
+ gck_list_unref_free (modules);
+
+ /* A self-signed certificate */
+ contents = testing_data_read ("der-certificate.crt", &n_contents);
+ cert_self = gcr_simple_certificate_new (contents, n_contents);
+ g_free (contents);
+
+ /* A signed certificate */
+ contents = testing_data_read ("dhansak-collabora.cer", &n_contents);
+ cert_signed = mock_certificate_new (contents, n_contents);
+ g_free (contents);
+
+ /* The signer for the above certificate */
+ contents = testing_data_read ("collabora-ca.cer", &n_contents);
+ cert_ca = mock_certificate_new (contents, n_contents);
+ g_free (contents);
+}
+
+static void
+add_certificate_to_module (GcrCertificate *certificate)
+{
+ GckAttributes *attrs;
+ gconstpointer data;
+ gsize n_data, n_subject;
+ gpointer subject;
+
+ data = gcr_certificate_get_der_data (certificate, &n_data);
+ g_assert (data);
+
+ subject = gcr_certificate_get_subject_raw (certificate, &n_subject);
+ g_assert (subject);
+
+ /* Add a certificate to the module */
+ attrs = gck_attributes_new ();
+ gck_attributes_add_data (attrs, CKA_VALUE, data, n_data);
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_CERTIFICATE);
+ gck_attributes_add_ulong (attrs, CKA_CERTIFICATE_TYPE, CKC_X_509);
+ gck_attributes_add_data (attrs, CKA_SUBJECT, subject, n_subject);
+ gck_mock_module_take_object (attrs);
+
+ g_free (subject);
+}
+
+static void
+add_anchor_to_module (GcrCertificate *certificate, const gchar *purpose)
+{
+ GckAttributes *attrs;
+ gconstpointer data;
+ gsize n_data;
+
+ data = gcr_certificate_get_der_data (certificate, &n_data);
+ g_assert (data);
+
+ /* And add a certificate exception for the signed certificate */
+ attrs = gck_attributes_new ();
+ gck_attributes_add_data (attrs, CKA_G_CERTIFICATE_VALUE, data, n_data);
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_G_TRUST_ASSERTION);
+ gck_attributes_add_ulong (attrs, CKA_G_ASSERTION_TYPE, CKT_G_CERTIFICATE_TRUST_ANCHOR);
+ gck_attributes_add_string (attrs, CKA_G_PURPOSE, purpose);
+ gck_mock_module_take_object (attrs);
+}
+
+static void
+add_pinned_to_module (GcrCertificate *certificate, const gchar *purpose, const gchar *host)
+{
+ GckAttributes *attrs;
+ gconstpointer data;
+ gsize n_data;
+
+ data = gcr_certificate_get_der_data (certificate, &n_data);
+ g_assert (data);
+
+ /* And add a certificate exception for the signed certificate */
+ attrs = gck_attributes_new ();
+ gck_attributes_add_data (attrs, CKA_G_CERTIFICATE_VALUE, data, n_data);
+ gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_G_TRUST_ASSERTION);
+ gck_attributes_add_ulong (attrs, CKA_G_ASSERTION_TYPE, CKT_G_CERTIFICATE_TRUST_EXCEPTION);
+ gck_attributes_add_string (attrs, CKA_G_PURPOSE, purpose);
+ gck_attributes_add_string (attrs, CKA_G_PEER, host);
+ gck_mock_module_take_object (attrs);
+}
+
+TESTING_TEARDOWN (certificate_chain)
+{
+ CK_RV rv;
+
+ g_object_unref (cert_self);
+ cert_self = NULL;
+
+ g_object_unref (cert_signed);
+ cert_signed = NULL;
+
+ g_object_unref (cert_ca);
+ cert_signed = NULL;
+
+ rv = (funcs.C_Finalize) (NULL);
+ gck_assert_cmprv (rv, ==, CKR_OK);
+}
+
+TESTING_TEST (certificate_chain_new)
+{
+ GcrCertificateChain *chain;
+
+ chain = gcr_certificate_chain_new ();
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 0);
+
+ g_assert (gcr_certificate_chain_get_endpoint (chain) == NULL);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_new_with_cert)
+{
+ GcrCertificateChain *chain;
+ GcrCertificate *check;
+ guint type, length;
+
+ chain = gcr_certificate_chain_new ();
+ gcr_certificate_chain_add (chain, cert_signed);
+ gcr_certificate_chain_add (chain, cert_ca);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+
+ type = G_MAXUINT;
+ length = 0;
+ g_object_get (chain, "type", &type, "length", &length, NULL);
+ g_assert_cmpuint (type, ==, GCR_CERTIFICATE_CHAIN_UNKNOWN);
+ g_assert_cmpuint (length, ==, 2);
+
+ check = gcr_certificate_chain_get_certificate (chain, 1);
+ g_assert (check == cert_ca);
+
+ /* Not yet completed */
+ check = gcr_certificate_chain_get_anchor (chain);
+ g_assert (check == NULL);
+
+ check = gcr_certificate_chain_get_endpoint (chain);
+ g_assert (check == cert_signed);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_selfsigned)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Add a self-signed certificate */
+ gcr_certificate_chain_add (chain, cert_self);
+
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_SELFSIGNED);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_incomplete)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Add a signed certificate */
+ gcr_certificate_chain_add (chain, cert_signed);
+
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_INCOMPLETE);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_empty)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Add no certificate */
+
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_trim_extras)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Add two unrelated certificates */
+ gcr_certificate_chain_add (chain, cert_self);
+ gcr_certificate_chain_add (chain, cert_signed);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_SELFSIGNED);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 1);
+
+ g_object_unref (chain);
+}
+
+static void
+fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ *((GAsyncResult**)user_data) = result;
+ g_object_ref (result);
+ testing_wait_stop ();
+}
+
+TESTING_TEST (certificate_chain_complete_async)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+ GAsyncResult *result;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Add a whole bunch of certificates */
+ gcr_certificate_chain_add (chain, cert_signed);
+ gcr_certificate_chain_add (chain, cert_ca);
+ gcr_certificate_chain_add (chain, cert_self);
+
+ gcr_certificate_chain_build_async (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, fetch_async_result, &result);
+ testing_wait_until (500);
+ if (!gcr_certificate_chain_build_finish (chain, result, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+ g_object_unref (result);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_SELFSIGNED);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_with_anchor)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Two certificates in chain with ca trust anchor */
+ gcr_certificate_chain_add (chain, cert_signed);
+ gcr_certificate_chain_add (chain, cert_ca);
+ add_anchor_to_module (cert_ca, GCR_PURPOSE_CLIENT_AUTH);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_ANCHORED);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+ g_assert (gcr_certificate_chain_get_anchor (chain) == cert_ca);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_with_anchor_and_lookup_ca)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* One signed certificate, with CA in pkcs11, and trust anchor */
+ gcr_certificate_chain_add (chain, cert_signed);
+ add_certificate_to_module (cert_ca);
+ add_anchor_to_module (cert_ca, GCR_PURPOSE_CLIENT_AUTH);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 1);
+
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_ANCHORED);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+ g_assert (gcr_certificate_chain_get_anchor (chain) != NULL);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_with_pinned)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* One certificate, and add CA to pkcs11 */
+ gcr_certificate_chain_add (chain, cert_signed);
+ gcr_certificate_chain_add (chain, cert_ca);
+ add_pinned_to_module (cert_signed, GCR_PURPOSE_CLIENT_AUTH, "pinned.example.com");
+
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 2);
+
+ /* But we don't allow the lookup to happen */
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ "pinned.example.com", 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_PINNED);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 1);
+ g_assert (gcr_certificate_chain_get_anchor (chain) == NULL);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_without_lookups)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* One certificate, and add CA to pkcs11 */
+ gcr_certificate_chain_add (chain, cert_signed);
+ add_certificate_to_module (cert_ca);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 1);
+
+ /* But we don't allow the lookup to happen */
+ if (!gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, GCR_CERTIFICATE_CHAIN_FLAG_NO_LOOKUPS,
+ NULL, &error))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_INCOMPLETE);
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 1);
+ g_assert (gcr_certificate_chain_get_anchor (chain) == NULL);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_with_lookup_error)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ /* Make the lookup fail */
+ funcs.C_GetAttributeValue = gck_mock_fail_C_GetAttributeValue;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Two certificates in chain with ca trust anchor */
+ gcr_certificate_chain_add (chain, cert_signed);
+ add_certificate_to_module (cert_ca);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_length (chain), ==, 1);
+
+ if (gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_error (error, GCK_ERROR, CKR_FUNCTION_FAILED);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_with_anchor_error)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+
+ /* Make the lookup fail */
+ funcs.C_GetAttributeValue = gck_mock_fail_C_GetAttributeValue;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Two certificates in chain with ca trust anchor */
+ gcr_certificate_chain_add (chain, cert_signed);
+ add_certificate_to_module (cert_ca);
+
+ if (gcr_certificate_chain_build (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, &error))
+ g_assert_not_reached ();
+ g_assert_error (error, GCK_ERROR, CKR_FUNCTION_FAILED);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN);
+
+ g_object_unref (chain);
+}
+
+TESTING_TEST (certificate_chain_with_anchor_error_async)
+{
+ GcrCertificateChain *chain;
+ GError *error = NULL;
+ GAsyncResult *result;
+
+ /* Make the lookup fail */
+ funcs.C_GetAttributeValue = gck_mock_fail_C_GetAttributeValue;
+
+ chain = gcr_certificate_chain_new ();
+
+ /* Two certificates in chain with ca trust anchor */
+ gcr_certificate_chain_add (chain, cert_signed);
+ add_certificate_to_module (cert_ca);
+
+ gcr_certificate_chain_build_async (chain, GCR_PURPOSE_CLIENT_AUTH,
+ NULL, 0, NULL, fetch_async_result, &result);
+ testing_wait_until (500);
+ if (gcr_certificate_chain_build_finish (chain, result, &error))
+ g_assert_not_reached ();
+ g_assert_error (error, GCK_ERROR, CKR_FUNCTION_FAILED);
+ g_object_unref (result);
+
+ g_assert_cmpuint (gcr_certificate_chain_get_chain_type (chain), ==,
+ GCR_CERTIFICATE_CHAIN_UNKNOWN);
+
+ g_object_unref (chain);
+}
diff --git a/gcr/tests/test-data/collabora-ca.cer b/gcr/tests/test-data/collabora-ca.cer
new file mode 100644
index 0000000..2842c69
Binary files /dev/null and b/gcr/tests/test-data/collabora-ca.cer differ
diff --git a/gcr/tests/test-data/dhansak-collabora.cer b/gcr/tests/test-data/dhansak-collabora.cer
new file mode 100644
index 0000000..c411e7d
Binary files /dev/null and b/gcr/tests/test-data/dhansak-collabora.cer differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]