summaryrefslogtreecommitdiff
path: root/gcr
diff options
context:
space:
mode:
authorCorentin Noël <corentin.noel@collabora.com>2022-05-10 16:08:39 +0200
committerNiels De Graef <nielsdegraef@gmail.com>2022-08-07 20:32:12 +0000
commit46d67a18ba3e64297f95f7a900eeb9da24afd452 (patch)
tree6bea618a7022edc1210247344f0dba9c5bfd54e7 /gcr
parente934b5bb7642023b5a880a71e00997fccc12a262 (diff)
downloadgcr-46d67a18ba3e64297f95f7a900eeb9da24afd452.tar.gz
gcr: Add GcrCertificateSection/Field
Allows to use the same code-path for both GTK3 and GTK4. Also allow any library-user to reimplement it with the toolkit of its choice or to adapt it when a different styling is required (e.g. with libadwaita).
Diffstat (limited to 'gcr')
-rw-r--r--gcr/gcr-certificate-field-private.h75
-rw-r--r--gcr/gcr-certificate-field.c261
-rw-r--r--gcr/gcr-certificate-field.h32
-rw-r--r--gcr/gcr-certificate-section.c211
-rw-r--r--gcr/gcr-certificate-section.h36
-rw-r--r--gcr/gcr-certificate.c435
-rw-r--r--gcr/gcr-certificate.h2
-rw-r--r--gcr/gcr.h2
-rw-r--r--gcr/meson.build4
-rw-r--r--gcr/test-certificate.c33
10 files changed, 1091 insertions, 0 deletions
diff --git a/gcr/gcr-certificate-field-private.h b/gcr/gcr-certificate-field-private.h
new file mode 100644
index 0000000..bfd5bcf
--- /dev/null
+++ b/gcr/gcr-certificate-field-private.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 Collabora Ltd.
+ * Copyright Corentin Noël <corentin.noel@collabora.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GCR_CERTIFICATE_FIELD_PRIVATE_H__
+#define __GCR_CERTIFICATE_FIELD_PRIVATE_H__
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#include "gcr-types.h"
+#include "gcr-certificate-field.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GcrCertificateSection *_gcr_certificate_section_new (const char *label,
+ gboolean important);
+void _gcr_certificate_section_append_field (GcrCertificateSection *section,
+ GcrCertificateField *field);
+GcrCertificateField *_gcr_certificate_field_new_take_value (GcrCertificateSection *section,
+ const char *label,
+ char *value);
+GcrCertificateField *_gcr_certificate_field_new_take_values (GcrCertificateSection *section,
+ const char *label,
+ GStrv value);
+GcrCertificateField *_gcr_certificate_field_new_take_bytes (GcrCertificateSection *section,
+ const char *label,
+ GBytes *bytes);
+
+static inline void
+_gcr_certificate_section_new_field_take_value (GcrCertificateSection *section,
+ const char *label,
+ char *value)
+{
+ GcrCertificateField *field = _gcr_certificate_field_new_take_value (section, label, value);
+ _gcr_certificate_section_append_field (section, field);
+ g_object_unref (field);
+}
+
+static inline void
+_gcr_certificate_section_new_field_take_values (GcrCertificateSection *section,
+ const char *label,
+ GStrv values)
+{
+ GcrCertificateField *field = _gcr_certificate_field_new_take_values (section, label, values);
+ _gcr_certificate_section_append_field (section, field);
+ g_object_unref (field);
+}
+
+static inline void
+_gcr_certificate_section_new_field_take_bytes (GcrCertificateSection *section,
+ const char *label,
+ GBytes *bytes)
+{
+ GcrCertificateField *field = _gcr_certificate_field_new_take_bytes (section, label, bytes);
+ _gcr_certificate_section_append_field (section, field);
+ g_object_unref (field);
+}
+
+static inline void
+_gcr_certificate_section_new_field (GcrCertificateSection *section,
+ const char *label,
+ const char *value)
+{
+ _gcr_certificate_section_new_field_take_value (section, label, g_strdup (value));
+}
+
+G_END_DECLS
+
+#endif /* __GCR_CERTIFICATE_FIELD_PRIVATE_H__ */
diff --git a/gcr/gcr-certificate-field.c b/gcr/gcr-certificate-field.c
new file mode 100644
index 0000000..3869e19
--- /dev/null
+++ b/gcr/gcr-certificate-field.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2021 Collabora Ltd.
+ * Copyright Corentin Noël <corentin.noel@collabora.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "gcr-certificate-field.h"
+#include "gcr-certificate-field-private.h"
+#include "gcr-enum-types.h"
+
+struct _GcrCertificateField
+{
+ GObject parent_instance;
+
+ char *label;
+ GValue value;
+ GcrCertificateSection *section;
+};
+
+G_DEFINE_FINAL_TYPE (GcrCertificateField, gcr_certificate_field, G_TYPE_OBJECT)
+
+enum {
+ PROP_LABEL = 1,
+ PROP_VALUE,
+ PROP_SECTION,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties [N_PROPERTIES];
+
+static void
+gcr_certificate_field_finalize (GObject *object)
+{
+ GcrCertificateField *self = (GcrCertificateField *)object;
+
+ g_clear_pointer (&self->label, g_free);
+ g_value_unset (&self->value);
+
+ G_OBJECT_CLASS (gcr_certificate_field_parent_class)->finalize (object);
+}
+
+static void
+gcr_certificate_field_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateField *self = GCR_CERTIFICATE_FIELD (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ g_value_set_string (value, self->label);
+ break;
+ case PROP_VALUE:
+ g_value_set_boxed (value, &self->value);
+ break;
+ case PROP_SECTION:
+ g_value_set_object (value, self->section);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcr_certificate_field_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateField *self = GCR_CERTIFICATE_FIELD (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ self->label = g_value_dup_string (value);
+ break;
+ case PROP_SECTION:
+ self->section = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcr_certificate_field_class_init (GcrCertificateFieldClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gcr_certificate_field_finalize;
+ object_class->get_property = gcr_certificate_field_get_property;
+ object_class->set_property = gcr_certificate_field_set_property;
+
+ obj_properties[PROP_LABEL] =
+ g_param_spec_string ("label",
+ "Label",
+ "Display name of the field.",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ obj_properties[PROP_VALUE] =
+ g_param_spec_boxed ("value",
+ "Value",
+ "Display name of the value.",
+ G_TYPE_VALUE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ obj_properties[PROP_SECTION] =
+ g_param_spec_object ("section",
+ "Section",
+ "The section it is included.",
+ GCR_TYPE_CERTIFICATE_SECTION,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class,
+ N_PROPERTIES,
+ obj_properties);
+}
+
+static void
+gcr_certificate_field_init (GcrCertificateField *self)
+{
+}
+
+/**
+ * gcr_certificate_field_get_label:
+ * @self: the #GcrCertificateField
+ *
+ * Get the display label of the field.
+ *
+ * Returns: the display label of the field
+ */
+const char *
+gcr_certificate_field_get_label (GcrCertificateField *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_FIELD (self), NULL);
+
+ return self->label;
+}
+
+/**
+ * gcr_certificate_field_get_value:
+ * @self: the #GcrCertificateField
+ * @value: (out caller-allocates): the `GValue` to fill
+ *
+ * Get the value of the field.
+ *
+ * The @value will have been initialized to the `GType` the value should be
+ * provided in.
+ *
+ * Returns: %TRUE if the value was set successfully.
+ */
+gboolean
+gcr_certificate_field_get_value (GcrCertificateField *self,
+ GValue *value)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_FIELD (self), FALSE);
+ g_return_val_if_fail (G_IS_VALUE (value), FALSE);
+
+ if (G_VALUE_HOLDS (&self->value, G_VALUE_TYPE (value))) {
+ g_value_copy (&self->value, value);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gcr_certificate_field_get_value_type:
+ * @self: the #GcrCertificateField
+ *
+ * Get the type associated with the value.
+ *
+ * Returns: The `GType` of the value
+ */
+GType
+gcr_certificate_field_get_value_type (GcrCertificateField *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_FIELD (self), FALSE);
+
+ return G_VALUE_TYPE (&self->value);
+}
+
+/**
+ * gcr_certificate_field_get_section:
+ * @self: the #GcrCertificateField
+ *
+ * Get the parent #GcrCertificateSection.
+ *
+ * Returns: (transfer none): the parent #GcrCertificateSection
+ */
+GcrCertificateSection *
+gcr_certificate_field_get_section (GcrCertificateField *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_FIELD (self), NULL);
+
+ return self->section;
+}
+
+GcrCertificateField *
+_gcr_certificate_field_new_take_value (GcrCertificateSection *section,
+ const char *label,
+ char *value)
+{
+ GcrCertificateField *self;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_SECTION (section), NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+ g_return_val_if_fail (value != NULL, NULL);
+
+ self = g_object_new (GCR_TYPE_CERTIFICATE_FIELD,
+ "section", section,
+ "label", label,
+ NULL);
+ g_value_init (&self->value, G_TYPE_STRING);
+ g_value_take_string (&self->value, value);
+
+ return self;
+}
+
+GcrCertificateField *
+_gcr_certificate_field_new_take_values (GcrCertificateSection *section,
+ const char *label,
+ GStrv values)
+{
+ GcrCertificateField *self;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_SECTION (section), NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+ g_return_val_if_fail (values != NULL, NULL);
+
+ self = g_object_new (GCR_TYPE_CERTIFICATE_FIELD,
+ "section", section,
+ "label", label,
+ NULL);
+ g_value_init (&self->value, G_TYPE_STRV);
+ g_value_take_boxed (&self->value, values);
+
+ return self;
+}
+
+GcrCertificateField *
+_gcr_certificate_field_new_take_bytes (GcrCertificateSection *section,
+ const char *label,
+ GBytes *bytes)
+{
+ GcrCertificateField *self;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_SECTION (section), NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+ g_return_val_if_fail (bytes != NULL, NULL);
+
+ self = g_object_new (GCR_TYPE_CERTIFICATE_FIELD,
+ "section", section,
+ "label", label,
+ NULL);
+ g_value_init (&self->value, G_TYPE_BYTES);
+ g_value_take_boxed (&self->value, bytes);
+
+ return self;
+}
diff --git a/gcr/gcr-certificate-field.h b/gcr/gcr-certificate-field.h
new file mode 100644
index 0000000..42e3f82
--- /dev/null
+++ b/gcr/gcr-certificate-field.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Collabora Ltd.
+ * Copyright Corentin Noël <corentin.noel@collabora.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GCR_CERTIFICATE_FIELD_H__
+#define __GCR_CERTIFICATE_FIELD_H__
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#include <gcr/gcr-certificate-section.h>
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_CERTIFICATE_FIELD (gcr_certificate_field_get_type ())
+
+G_DECLARE_FINAL_TYPE (GcrCertificateField, gcr_certificate_field, GCR, CERTIFICATE_FIELD, GObject)
+
+const char *gcr_certificate_field_get_label (GcrCertificateField *self);
+gboolean gcr_certificate_field_get_value (GcrCertificateField *self,
+ GValue *value);
+GType gcr_certificate_field_get_value_type (GcrCertificateField *self);
+GcrCertificateSection *gcr_certificate_field_get_section (GcrCertificateField *self);
+
+G_END_DECLS
+
+#endif /* __GCR_CERTIFICATE_FIELD_H__ */
diff --git a/gcr/gcr-certificate-section.c b/gcr/gcr-certificate-section.c
new file mode 100644
index 0000000..4741ca6
--- /dev/null
+++ b/gcr/gcr-certificate-section.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2021 Collabora Ltd.
+ * Copyright Corentin Noël <corentin.noel@collabora.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "gcr-certificate-field.h"
+#include "gcr-certificate-section.h"
+#include "gcr-enum-types.h"
+
+struct _GcrCertificateSection {
+ GObject parent_instance;
+
+ char *label;
+ GcrCertificateSectionFlags flags;
+ GListStore *fields;
+};
+
+G_DEFINE_FINAL_TYPE (GcrCertificateSection, gcr_certificate_section, G_TYPE_OBJECT)
+
+enum {
+ PROP_LABEL = 1,
+ PROP_FIELDS,
+ PROP_FLAGS,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties [N_PROPERTIES];
+
+
+/**
+ * _gcr_certificate_section_new:
+ * @label: the user-displayable label of the section
+ * @important: whether this section should be visible by default
+ *
+ * Create a new certificate section usable in user interfaces.
+ *
+ * Returns: (transfer full): a new #GcrCertificateSection
+ */
+GcrCertificateSection *
+_gcr_certificate_section_new (const char *label,
+ gboolean important)
+{
+ return g_object_new (GCR_TYPE_CERTIFICATE_SECTION,
+ "label", label,
+ "flags", important ? GCR_CERTIFICATE_SECTION_IMPORTANT : GCR_CERTIFICATE_SECTION_NONE,
+ NULL);
+}
+
+static void
+gcr_certificate_section_finalize (GObject *object)
+{
+ GcrCertificateSection *self = (GcrCertificateSection *)object;
+ g_clear_pointer (&self->label, g_free);
+
+ G_OBJECT_CLASS (gcr_certificate_section_parent_class)->finalize (object);
+}
+
+static void
+gcr_certificate_section_dispose (GObject *object)
+{
+ GcrCertificateSection *self = (GcrCertificateSection *)object;
+ g_clear_object (&self->fields);
+
+ G_OBJECT_CLASS (gcr_certificate_section_parent_class)->dispose (object);
+}
+
+static void
+gcr_certificate_section_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateSection *self = GCR_CERTIFICATE_SECTION (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ g_value_set_string (value, self->label);
+ break;
+ case PROP_FIELDS:
+ g_value_set_object (value, &self->fields);
+ break;
+ case PROP_FLAGS:
+ g_value_set_flags (value, self->flags);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcr_certificate_section_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateSection *self = GCR_CERTIFICATE_SECTION (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ self->label = g_value_dup_string (value);
+ break;
+ case PROP_FLAGS:
+ self->flags = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gcr_certificate_section_class_init (GcrCertificateSectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gcr_certificate_section_finalize;
+ object_class->dispose = gcr_certificate_section_dispose;
+ object_class->get_property = gcr_certificate_section_get_property;
+ object_class->set_property = gcr_certificate_section_set_property;
+
+ obj_properties[PROP_LABEL] =
+ g_param_spec_string ("label",
+ "Label",
+ "Display name of the field.",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ obj_properties[PROP_FIELDS] =
+ g_param_spec_object ("fields",
+ "Fields",
+ "The list of fields.",
+ G_TYPE_LIST_MODEL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ obj_properties[PROP_FLAGS] =
+ g_param_spec_flags ("flags",
+ "Flags",
+ "Flags defined for the section.",
+ GCR_TYPE_CERTIFICATE_SECTION_FLAGS,
+ GCR_CERTIFICATE_SECTION_NONE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class,
+ N_PROPERTIES,
+ obj_properties);
+}
+
+static void
+gcr_certificate_section_init (GcrCertificateSection *self)
+{
+ self->fields = g_list_store_new (GCR_TYPE_CERTIFICATE_FIELD);
+}
+
+/**
+ * gcr_certificate_section_get_label:
+ * @self: the #GcrCertificateSection
+ *
+ * Get the displayable label of the section.
+ *
+ * Returns: the displayable label of the section
+ */
+const char *
+gcr_certificate_section_get_label (GcrCertificateSection *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return self->label;
+}
+
+/**
+ * gcr_certificate_section_get_flags:
+ * @self: the #GcrCertificateSection
+ *
+ * Get the flags.
+ *
+ * Returns: the `GcrCertificateSectionFlags`
+ */
+GcrCertificateSectionFlags
+gcr_certificate_section_get_flags (GcrCertificateSection *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ return self->flags;
+}
+
+/**
+ * gcr_certificate_section_get_fields:
+ * @self: the #GcrCertificateSection
+ *
+ * Get the list of all the fields in this section.
+ *
+ * Returns: (transfer none): a #GListModel of #GcrCertificateField
+ */
+GListModel *
+gcr_certificate_section_get_fields (GcrCertificateSection *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return G_LIST_MODEL (self->fields);
+}
+
+void
+_gcr_certificate_section_append_field (GcrCertificateSection *section,
+ GcrCertificateField *field)
+{
+ g_return_if_fail (GCR_IS_CERTIFICATE_SECTION (section));
+ g_return_if_fail (GCR_IS_CERTIFICATE_FIELD (field));
+
+ g_list_store_append (section->fields, field);
+}
+
diff --git a/gcr/gcr-certificate-section.h b/gcr/gcr-certificate-section.h
new file mode 100644
index 0000000..92267fc
--- /dev/null
+++ b/gcr/gcr-certificate-section.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 Collabora Ltd.
+ * Copyright Corentin Noël <corentin.noel@collabora.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GCR_CERTIFICATE_SECTION_H__
+#define __GCR_CERTIFICATE_SECTION_H__
+
+#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#include <gcr/gcr-enum-types.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_CERTIFICATE_SECTION (gcr_certificate_section_get_type())
+
+G_DECLARE_FINAL_TYPE (GcrCertificateSection, gcr_certificate_section, GCR, CERTIFICATE_SECTION, GObject)
+
+typedef enum {
+ GCR_CERTIFICATE_SECTION_NONE = 0,
+ GCR_CERTIFICATE_SECTION_IMPORTANT = 1 << 0,
+} GcrCertificateSectionFlags;
+
+const char *gcr_certificate_section_get_label (GcrCertificateSection *self);
+GListModel *gcr_certificate_section_get_fields (GcrCertificateSection *self);
+GcrCertificateSectionFlags gcr_certificate_section_get_flags (GcrCertificateSection *self);
+
+G_END_DECLS
+
+#endif /* __GCR_CERTIFICATE_SECTION_H__ */
diff --git a/gcr/gcr-certificate.c b/gcr/gcr-certificate.c
index fc2d9e0..a11cf1e 100644
--- a/gcr/gcr-certificate.c
+++ b/gcr/gcr-certificate.c
@@ -21,6 +21,9 @@
#include "gcr-certificate.h"
#include "gcr-certificate-extensions.h"
+#include "gcr-certificate-field.h"
+#include "gcr-certificate-field-private.h"
+#include "gcr-fingerprint.h"
#include "gcr-internal.h"
#include "gcr-subject-public-key.h"
@@ -30,6 +33,7 @@
#include "egg/egg-asn1-defs.h"
#include "egg/egg-dn.h"
#include "egg/egg-hex.h"
+#include "egg/egg-oid.h"
#include <string.h>
#include <glib/gi18n-lib.h>
@@ -856,6 +860,437 @@ gcr_certificate_get_basic_constraints (GcrCertificate *self,
return TRUE;
}
+static void
+append_subject_public_key (GcrCertificate *self,
+ GcrCertificateSection *section,
+ GNode *subject_public_key)
+{
+ guint key_nbits;
+ const gchar *text;
+ gchar *display;
+ GBytes *value;
+ guchar *raw;
+ gsize n_raw;
+ GQuark oid;
+ guint bits;
+
+ key_nbits = gcr_certificate_get_key_size (self);
+
+ oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (subject_public_key,
+ "algorithm", "algorithm", NULL));
+ text = egg_oid_get_description (oid);
+ _gcr_certificate_section_new_field (section, _("Key Algorithm"), text);
+
+ value = egg_asn1x_get_element_raw (egg_asn1x_node (subject_public_key,
+ "algorithm", "parameters", NULL));
+ if (value) {
+ _gcr_certificate_section_new_field_take_bytes (section,
+ _("Key Parameters"),
+ g_steal_pointer (&value));
+ }
+
+ if (key_nbits > 0) {
+ display = g_strdup_printf ("%u", key_nbits);
+ _gcr_certificate_section_new_field_take_value (section,
+ _("Key Size"),
+ g_steal_pointer (&display));
+ }
+
+ value = egg_asn1x_get_element_raw (subject_public_key);
+ raw = gcr_fingerprint_from_subject_public_key_info (g_bytes_get_data (value, NULL),
+ g_bytes_get_size (value),
+ G_CHECKSUM_SHA1, &n_raw);
+ g_clear_pointer (&value, g_bytes_unref);
+ _gcr_certificate_section_new_field_take_bytes (section,
+ _("Key SHA1 Fingerprint"),
+ g_bytes_new_take (raw, n_raw));
+
+ value = egg_asn1x_get_bits_as_raw (egg_asn1x_node (subject_public_key, "subjectPublicKey", NULL), &bits);
+ _gcr_certificate_section_new_field_take_bytes (section, _("Public Key"), g_steal_pointer (&value));
+}
+
+static GcrCertificateSection *
+append_extension_basic_constraints (GBytes *data)
+{
+ GcrCertificateSection *section;
+ gboolean is_ca = FALSE;
+ gint path_len = -1;
+ gchar *number;
+
+ if (!_gcr_certificate_extension_basic_constraints (data, &is_ca, &path_len))
+ return NULL;
+
+ section = _gcr_certificate_section_new (_("Basic Constraints"), FALSE);
+ _gcr_certificate_section_new_field (section, _("Certificate Authority"), is_ca ? _("Yes") : _("No"));
+
+ if (path_len < 0)
+ number = g_strdup (_("Unlimited"));
+ else
+ number = g_strdup_printf ("%d", path_len);
+
+ _gcr_certificate_section_new_field_take_value (section, _("Max Path Length"), g_steal_pointer (&number));
+
+ return section;
+}
+
+static GcrCertificateSection *
+append_extension_extended_key_usage (GBytes *data)
+{
+ GcrCertificateSection *section;
+ GQuark *oids;
+ GStrvBuilder *text;
+ guint i;
+
+ oids = _gcr_certificate_extension_extended_key_usage (data);
+ if (!oids)
+ return NULL;
+
+ text = g_strv_builder_new ();
+ for (i = 0; oids[i] != 0; i++) {
+ g_strv_builder_add (text, egg_oid_get_description (oids[i]));
+ }
+
+ g_free (oids);
+
+ section = _gcr_certificate_section_new (_("Extended Key Usage"), FALSE);
+ _gcr_certificate_section_new_field_take_values (section, _("Allowed Purposes"), g_strv_builder_end (text));
+ g_strv_builder_unref (text);
+
+ return section;
+}
+
+static GcrCertificateSection *
+append_extension_subject_key_identifier (GBytes *data)
+{
+ GcrCertificateSection *section;
+ gpointer keyid;
+ gsize n_keyid;
+
+ keyid = _gcr_certificate_extension_subject_key_identifier (data, &n_keyid);
+ if (!keyid)
+ return NULL;
+
+ section = _gcr_certificate_section_new (_("Subject Key Identifier"), FALSE);
+ gchar *display = egg_hex_encode_full (keyid, n_keyid, TRUE, " ", 1);
+ g_free (keyid);
+ _gcr_certificate_section_new_field_take_value (section, _("Key Identifier"), g_steal_pointer (&display));
+
+ return section;
+}
+
+static const struct {
+ guint usage;
+ const gchar *description;
+} usage_descriptions[] = {
+ { GCR_KEY_USAGE_DIGITAL_SIGNATURE, N_("Digital signature") },
+ { GCR_KEY_USAGE_NON_REPUDIATION, N_("Non repudiation") },
+ { GCR_KEY_USAGE_KEY_ENCIPHERMENT, N_("Key encipherment") },
+ { GCR_KEY_USAGE_DATA_ENCIPHERMENT, N_("Data encipherment") },
+ { GCR_KEY_USAGE_KEY_AGREEMENT, N_("Key agreement") },
+ { GCR_KEY_USAGE_KEY_CERT_SIGN, N_("Certificate signature") },
+ { GCR_KEY_USAGE_CRL_SIGN, N_("Revocation list signature") },
+ { GCR_KEY_USAGE_ENCIPHER_ONLY, N_("Encipher only") },
+ { GCR_KEY_USAGE_DECIPHER_ONLY, N_("Decipher only") }
+};
+
+static GcrCertificateSection *
+append_extension_key_usage (GBytes *data)
+{
+ GcrCertificateSection *section;
+ gulong key_usage;
+ GStrvBuilder *values;
+ guint i;
+
+ if (!_gcr_certificate_extension_key_usage (data, &key_usage))
+ return NULL;
+
+ values = g_strv_builder_new ();
+ for (i = 0; i < G_N_ELEMENTS (usage_descriptions); i++) {
+ if (key_usage & usage_descriptions[i].usage) {
+ g_strv_builder_add (values, _(usage_descriptions[i].description));
+ }
+ }
+
+ section = _gcr_certificate_section_new (_("Key Usage"), FALSE);
+ _gcr_certificate_section_new_field_take_values (section, _("Usages"), g_strv_builder_end (values));
+ g_strv_builder_unref (values);
+
+ return section;
+}
+
+static GcrCertificateSection *
+append_extension_subject_alt_name (GBytes *data)
+{
+ GcrCertificateSection *section;
+ GArray *general_names;
+ GcrGeneralName *general;
+ guint i;
+
+ general_names = _gcr_certificate_extension_subject_alt_name (data);
+ if (general_names == NULL)
+ return FALSE;
+
+ section = _gcr_certificate_section_new (_("Subject Alternative Names"), FALSE);
+
+ for (i = 0; i < general_names->len; i++) {
+ general = &g_array_index (general_names, GcrGeneralName, i);
+ if (general->display == NULL) {
+ _gcr_certificate_section_new_field_take_bytes (section, general->description, g_bytes_ref (general->raw));
+ } else
+ _gcr_certificate_section_new_field (section, general->description, general->display);
+ }
+
+ _gcr_general_names_free (general_names);
+
+ return section;
+}
+
+static GcrCertificateSection *
+append_extension_hex (GQuark oid,
+ GBytes *value)
+{
+ GcrCertificateSection *section;
+ const gchar *text;
+
+ section = _gcr_certificate_section_new (_("Extension"), FALSE);
+
+ /* Extension type */
+ text = egg_oid_get_description (oid);
+ _gcr_certificate_section_new_field (section, _("Identifier"), g_strdup (text));
+ _gcr_certificate_section_new_field_take_bytes (section, _("Value"), g_steal_pointer (&value));
+
+ return section;
+}
+
+static GcrCertificateSection *
+append_extension (GcrCertificate *self,
+ GNode *node)
+{
+ GQuark oid;
+ GBytes *value;
+ gboolean critical;
+ GcrCertificateSection *section = NULL;
+
+ /* Dig out the OID */
+ oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "extnID", NULL));
+ g_return_val_if_fail (oid, NULL);
+
+ /* Extension value */
+ value = egg_asn1x_get_string_as_bytes (egg_asn1x_node (node, "extnValue", NULL));
+
+ /* The custom parsers */
+ if (oid == GCR_OID_BASIC_CONSTRAINTS)
+ section = append_extension_basic_constraints (value);
+ else if (oid == GCR_OID_EXTENDED_KEY_USAGE)
+ section = append_extension_extended_key_usage (value);
+ else if (oid == GCR_OID_SUBJECT_KEY_IDENTIFIER)
+ section = append_extension_subject_key_identifier (value);
+ else if (oid == GCR_OID_KEY_USAGE)
+ section = append_extension_key_usage (value);
+ else if (oid == GCR_OID_SUBJECT_ALT_NAME)
+ section = append_extension_subject_alt_name (value);
+
+ /* Otherwise the default raw display */
+ if (!section) {
+ section = append_extension_hex (oid, g_steal_pointer (&value));
+ }
+
+ /* Critical */
+ if (section && egg_asn1x_get_boolean (egg_asn1x_node (node, "critical", NULL), &critical)) {
+ _gcr_certificate_section_new_field (section, _("Critical"), critical ? _("Yes") : _("No"));
+ }
+
+ g_clear_pointer (&value, g_bytes_unref);
+ return section;
+}
+
+static void
+on_parsed_dn_part (guint index,
+ GQuark oid,
+ GNode *value,
+ gpointer user_data)
+{
+ GcrCertificateSection *section = user_data;
+ const gchar *attr;
+ const gchar *desc;
+ gchar *label, *display;
+
+ attr = egg_oid_get_name (oid);
+ desc = egg_oid_get_description (oid);
+
+ /* Combine them into something sane */
+ if (attr && desc) {
+ if (strcmp (attr, desc) == 0)
+ label = g_strdup (attr);
+ else
+ label = g_strdup_printf ("%s (%s)", attr, desc);
+ } else if (!attr && !desc) {
+ label = g_strdup ("");
+ } else if (attr) {
+ label = g_strdup (attr);
+ } else if (desc) {
+ label = g_strdup (desc);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ display = egg_dn_print_value (oid, value);
+ if (!display)
+ display = g_strdup ("");
+
+ _gcr_certificate_section_new_field_take_value (section, label, g_steal_pointer (&display));
+ g_clear_pointer (&label, g_free);
+}
+
+/**
+ * gcr_certificate_get_interface_elements:
+ * @self: the #GcrCertificate
+ *
+ * Get the list of sections from the certificate that can be shown to the user
+ * interface.
+ *
+ * Returns: (element-type GcrCertificateSection) (transfer full): A #GList of
+ * #GcrCertificateSection
+ */
+GList *
+gcr_certificate_get_interface_elements (GcrCertificate *self)
+{
+ GcrCertificateSection *section;
+ GcrCertificateInfo *info;
+ GList *list = NULL;
+ gchar *display;
+ GBytes *bytes, *number;
+ GNode *subject_public_key;
+ GQuark oid;
+ GDateTime *datetime;
+ gulong version;
+ guint bits;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ info = certificate_info_load (self);
+ g_return_val_if_fail (info != NULL, NULL);
+
+ display = gcr_certificate_get_subject_name (self);
+ if (!display)
+ display = g_strdup (_("Certificate"));
+
+ section = _gcr_certificate_section_new (display, TRUE);
+ g_clear_pointer (&display, g_free);
+
+ display = gcr_certificate_get_subject_cn (self);
+ _gcr_certificate_section_new_field_take_value (section, _("Identity"), g_steal_pointer (&display));
+
+ display = gcr_certificate_get_issuer_cn (self);
+ _gcr_certificate_section_new_field_take_value (section, _("Verified by"), g_steal_pointer (&display));
+
+ datetime = gcr_certificate_get_expiry_date (self);
+ if (datetime) {
+ display = g_date_time_format (datetime, "%x");
+ if (display)
+ _gcr_certificate_section_new_field_take_value (section, _("Expires"), g_steal_pointer (&display));
+
+ g_clear_pointer (&datetime, g_date_time_unref);
+ }
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ /* The subject */
+ section = _gcr_certificate_section_new (_("Subject Name"), FALSE);
+ egg_dn_parse (egg_asn1x_node (info->asn1, "tbsCertificate", "subject", "rdnSequence", NULL), on_parsed_dn_part, section);
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ /* The Issuer */
+ section = _gcr_certificate_section_new (_("Issuer Name"), FALSE);
+ egg_dn_parse (egg_asn1x_node (info->asn1, "tbsCertificate", "issuer", "rdnSequence", NULL), on_parsed_dn_part, section);
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ /* The Issued Parameters */
+ section = _gcr_certificate_section_new (_("Issued Certificate"), FALSE);
+
+ if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (info->asn1, "tbsCertificate", "version", NULL), &version)) {
+ g_critical ("Unable to parse certificate version");
+ } else {
+ display = g_strdup_printf ("%lu", version + 1);
+ _gcr_certificate_section_new_field_take_value (section, _("Version"), g_steal_pointer (&display));
+ }
+
+ number = egg_asn1x_get_integer_as_raw (egg_asn1x_node (info->asn1, "tbsCertificate", "serialNumber", NULL));
+ if (!number) {
+ g_critical ("Unable to parse certificate serial number");
+ } else {
+ _gcr_certificate_section_new_field_take_bytes (section, _("Serial Number"), g_steal_pointer (&number));
+ }
+
+ datetime = gcr_certificate_get_issued_date (self);
+ if (datetime) {
+ display = g_date_time_format (datetime, "%x");
+ if (display)
+ _gcr_certificate_section_new_field_take_value (section, _("Not Valid Before"), g_steal_pointer (&display));
+
+ g_clear_pointer (&datetime, g_date_time_unref);
+ }
+
+ datetime = gcr_certificate_get_expiry_date (self);
+ if (datetime) {
+ display = g_date_time_format (datetime, "%x");
+ if (display)
+ _gcr_certificate_section_new_field_take_value (section, _("Not Valid After"), g_steal_pointer (&display));
+
+ g_clear_pointer (&datetime, g_date_time_unref);
+ }
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ /* Fingerprints */
+ bytes = g_bytes_new_static (info->der, info->n_der);
+ section = _gcr_certificate_section_new (_("Certificate Fingerprints"), FALSE);
+ display = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, bytes);
+ _gcr_certificate_section_new_field_take_value (section, "SHA1", g_steal_pointer (&display));
+ display = g_compute_checksum_for_bytes (G_CHECKSUM_MD5, bytes);
+ _gcr_certificate_section_new_field_take_value (section, "MD5", g_steal_pointer (&display));
+ g_clear_pointer (&bytes, g_bytes_unref);
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ /* Public Key Info */
+ section = _gcr_certificate_section_new (_("Public Key Info"), FALSE);
+ subject_public_key = egg_asn1x_node (info->asn1, "tbsCertificate", "subjectPublicKeyInfo", NULL);
+ append_subject_public_key (self, section, subject_public_key);
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ /* Extensions */
+ for (guint extension_num = 1; TRUE; ++extension_num) {
+ GNode *extension = egg_asn1x_node (info->asn1, "tbsCertificate", "extensions", extension_num, NULL);
+ if (extension == NULL)
+ break;
+ section = append_extension (self, extension);
+ if (section)
+ list = g_list_prepend (list, g_steal_pointer (&section));
+ }
+
+ /* Signature */
+ section = _gcr_certificate_section_new (_("Signature"), FALSE);
+
+ oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (info->asn1, "signatureAlgorithm", "algorithm", NULL));
+ _gcr_certificate_section_new_field (section, _("Signature Algorithm"), egg_oid_get_description (oid));
+
+ bytes = egg_asn1x_get_element_raw (egg_asn1x_node (info->asn1, "signatureAlgorithm", "parameters", NULL));
+ if (bytes) {
+ _gcr_certificate_section_new_field_take_bytes (section, _("Signature Parameters"), g_steal_pointer (&bytes));
+ }
+
+ bytes = egg_asn1x_get_bits_as_raw (egg_asn1x_node (info->asn1, "signature", NULL), &bits);
+ _gcr_certificate_section_new_field_take_bytes (section, _("Signature"), g_steal_pointer (&bytes));
+
+ list = g_list_prepend (list, g_steal_pointer (&section));
+
+ return g_list_reverse (list);
+}
+
/* -----------------------------------------------------------------------------
* MIXIN
*/
diff --git a/gcr/gcr-certificate.h b/gcr/gcr-certificate.h
index f0df372..a764d12 100644
--- a/gcr/gcr-certificate.h
+++ b/gcr/gcr-certificate.h
@@ -120,6 +120,8 @@ gboolean gcr_certificate_get_basic_constraints (GcrCertificate *self
gboolean *is_ca,
gint *path_len);
+GList* gcr_certificate_get_interface_elements (GcrCertificate *self);
+
void gcr_certificate_mixin_emit_notify (GcrCertificate *self);
void gcr_certificate_mixin_class_init (GObjectClass *object_class);
diff --git a/gcr/gcr.h b/gcr/gcr.h
index 17b4da0..b9705a8 100644
--- a/gcr/gcr.h
+++ b/gcr/gcr.h
@@ -34,7 +34,9 @@
#include <gcr/gcr-certificate.h>
#include <gcr/gcr-certificate-chain.h>
+#include <gcr/gcr-certificate-field.h>
#include <gcr/gcr-certificate-request.h>
+#include <gcr/gcr-certificate-section.h>
#include <gcr/gcr-enum-types.h>
#include <gcr/gcr-fingerprint.h>
#include <gcr/gcr-importer.h>
diff --git a/gcr/meson.build b/gcr/meson.build
index 6a1dd74..e67606e 100644
--- a/gcr/meson.build
+++ b/gcr/meson.build
@@ -4,7 +4,9 @@ gcr_headers_install_dir = gcr_headers_subdir / 'gcr'
gcr_public_sources = files(
'gcr-certificate.c',
'gcr-certificate-chain.c',
+ 'gcr-certificate-field.c',
'gcr-certificate-request.c',
+ 'gcr-certificate-section.c',
'gcr-fingerprint.c',
'gcr-importer.c',
'gcr-import-interaction.c',
@@ -43,7 +45,9 @@ gcr_headers = files(
'gcr.h',
'gcr-certificate.h',
'gcr-certificate-chain.h',
+ 'gcr-certificate-field.h',
'gcr-certificate-request.h',
+ 'gcr-certificate-section.h',
'gcr-fingerprint.h',
'gcr-importer.h',
'gcr-import-interaction.h',
diff --git a/gcr/test-certificate.c b/gcr/test-certificate.c
index 8bca034..cdaafe8 100644
--- a/gcr/test-certificate.c
+++ b/gcr/test-certificate.c
@@ -266,6 +266,38 @@ test_basic_constraints (Test *test,
g_assert (path_len == -1);
}
+
+static void
+test_interface_elements (Test *test,
+ gconstpointer unused)
+{
+ GList* sections = gcr_certificate_get_interface_elements (test->dsa_cert);
+ for (GList *l = sections; l != NULL; l = l->next) {
+ GcrCertificateSection *section = l->data;
+ GListModel *fields;
+
+ gcr_certificate_section_get_flags (section);
+ g_assert (gcr_certificate_section_get_label (section) != NULL);
+ fields = gcr_certificate_section_get_fields (section);
+ g_assert (fields != NULL);
+ g_assert (g_list_model_get_item_type (fields) == GCR_TYPE_CERTIFICATE_FIELD);
+ for (guint i = 0; i < g_list_model_get_n_items (fields); i++) {
+ GValue val = G_VALUE_INIT;
+ GType value_type;
+ GcrCertificateField *field = g_list_model_get_item (fields, i);
+ g_assert (gcr_certificate_field_get_label (field) != NULL);
+ value_type = gcr_certificate_field_get_value_type (field);
+ g_value_init (&val, value_type);
+ g_assert (gcr_certificate_field_get_value (field, &val));
+ g_value_unset (&val);
+ g_assert (gcr_certificate_field_get_section (field) == section);
+ g_object_unref (field);
+ }
+ }
+
+ g_list_free_full (sections, (GDestroyNotify) g_object_unref);
+}
+
static void
test_subject_alt_name (void)
{
@@ -346,6 +378,7 @@ main (int argc, char **argv)
g_test_add ("/gcr/certificate/key_size", Test, NULL, setup, test_certificate_key_size, teardown);
g_test_add ("/gcr/certificate/is_issuer", Test, NULL, setup, test_certificate_is_issuer, teardown);
g_test_add ("/gcr/certificate/basic_constraints", Test, NULL, setup, test_basic_constraints, teardown);
+ g_test_add ("/gcr/certificate/interface_elements", Test, NULL, setup, test_interface_elements, teardown);
g_test_add_func ("/gcr/certificate/subject_alt_name", test_subject_alt_name);
g_test_add_func ("/gcr/certificate/key_usage", test_key_usage);