diff options
author | Corentin Noël <corentin.noel@collabora.com> | 2021-10-14 12:28:53 +0200 |
---|---|---|
committer | Corentin Noël <corentin.noel@collabora.com> | 2021-10-14 12:28:53 +0200 |
commit | 14a959dbbc11cbdcc00c14e28db1c3b9bf12a1cc (patch) | |
tree | ba7fc8acc688c27e94afe1dbed6f1cf960707dab | |
parent | cb38e324007656f691fd8defd200e5d7aa2abeac (diff) | |
download | gcr-tintou/gtk4.tar.gz |
gcr-gtk4: Create GcrCertificateWidgettintou/gtk4
-rw-r--r-- | gcr-gtk4/gcr-certificate-widget.c | 683 | ||||
-rw-r--r-- | gcr-gtk4/gcr-certificate-widget.h | 30 | ||||
-rw-r--r-- | gcr-gtk4/gcr-gtk4.h | 15 | ||||
-rw-r--r-- | gcr-gtk4/gcr-renderer.c | 16 | ||||
-rw-r--r-- | gcr-gtk4/gcr-renderer.h | 25 | ||||
-rw-r--r-- | gcr-gtk4/gcr-section.c | 225 | ||||
-rw-r--r-- | gcr-gtk4/gcr-section.h | 30 | ||||
-rw-r--r-- | gcr-gtk4/libgcr-gtk4.map | 8 | ||||
-rw-r--r-- | gcr-gtk4/meson.build | 135 | ||||
-rw-r--r-- | gcr-gtk4/viewer/meson.build | 7 | ||||
-rw-r--r-- | gcr-gtk4/viewer/viewer.c | 93 | ||||
-rw-r--r-- | gcr/frob-parser.c | 2 | ||||
-rw-r--r-- | gcr/gcr-certificate-chain.c | 4 | ||||
-rw-r--r-- | gcr/gcr-simple-certificate.c | 88 | ||||
-rw-r--r-- | gcr/gcr-simple-certificate.h | 16 | ||||
-rw-r--r-- | gcr/gcr.h | 1 | ||||
-rw-r--r-- | gcr/test-certificate-chain.c | 3 | ||||
-rw-r--r-- | gcr/test-certificate.c | 24 | ||||
-rw-r--r-- | gcr/test-pkcs11-certificate.c | 10 | ||||
-rw-r--r-- | gcr/test-simple-certificate.c | 32 | ||||
-rw-r--r-- | gcr/test-trust.c | 3 | ||||
-rw-r--r-- | meson.build | 7 | ||||
-rw-r--r-- | meson_options.txt | 5 | ||||
-rw-r--r-- | ui/gcr-certificate-renderer.c | 14 | ||||
-rw-r--r-- | ui/gcr-viewer-tool.c | 1 |
25 files changed, 1397 insertions, 80 deletions
diff --git a/gcr-gtk4/gcr-certificate-widget.c b/gcr-gtk4/gcr-certificate-widget.c new file mode 100644 index 0000000..a1d69c6 --- /dev/null +++ b/gcr-gtk4/gcr-certificate-widget.c @@ -0,0 +1,683 @@ +/* + * Copyright 2021 Collabora Ltd. + * Copyright Corentin Noël <corentin.noel@collabora.com> + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include <gcr-gtk4/gcr-certificate-widget.h> +#include "gcr/gcr-certificate-extensions.h" +#include "gcr/gcr-oids.h" + +#include "gcr-section.h" + +#include "egg/egg-asn1x.h" +#include "egg/egg-asn1-defs.h" +#include "egg/egg-dn.h" +#include "egg/egg-oid.h" +#include "egg/egg-hex.h" + +struct _GcrCertificateWidget +{ + GtkWidget parent_instance; + + GcrCertificate *certificate; + GtkWidget *reveal_button; + GtkWidget *revealer; + GtkWidget *secondary_info; + GtkSizeGroup *size_group; +}; + +G_DEFINE_TYPE (GcrCertificateWidget, gcr_certificate_widget, GTK_TYPE_WIDGET) + +enum { + PROP_CERTIFICATE = 1, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +static void +gcr_certificate_widget_finalize (GObject *object) +{ + GtkWidget *child; + GcrCertificateWidget *self = (GcrCertificateWidget *)object; + + g_clear_object (&self->size_group); + while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) { + gtk_widget_unparent (child); + } + + G_OBJECT_CLASS (gcr_certificate_widget_parent_class)->finalize (object); +} + +static void +gcr_certificate_widget_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcrCertificateWidget *self = GCR_CERTIFICATE_WIDGET (object); + + switch (prop_id) + { + case PROP_CERTIFICATE: + g_value_set_object (value, gcr_certificate_widget_get_certificate (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_widget_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcrCertificateWidget *self = GCR_CERTIFICATE_WIDGET (object); + + switch (prop_id) + { + case PROP_CERTIFICATE: + gcr_certificate_widget_set_certificate (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcr_certificate_widget_class_init (GcrCertificateWidgetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = gcr_certificate_widget_finalize; + object_class->get_property = gcr_certificate_widget_get_property; + object_class->set_property = gcr_certificate_widget_set_property; + + obj_properties[PROP_CERTIFICATE] = + g_param_spec_object ("certificate", + "Certificate", + "Certificate to display.", + GCR_TYPE_CERTIFICATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, + N_PROPERTIES, + obj_properties); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); +} + +static void +on_reveal_button_clicked (GtkWidget *button, + GcrCertificateWidget *self) +{ + g_assert (GCR_IS_CERTIFICATE_WIDGET (self)); + gtk_widget_hide (button); + gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE); +} + +static void +gcr_certificate_widget_init (GcrCertificateWidget *self) +{ + gtk_orientable_set_orientation (GTK_ORIENTABLE (gtk_widget_get_layout_manager (GTK_WIDGET (self))), GTK_ORIENTATION_VERTICAL); + self->reveal_button = gtk_button_new_with_label ("…"); + gtk_widget_set_halign (self->reveal_button, GTK_ALIGN_CENTER); + gtk_widget_insert_before (self->reveal_button, GTK_WIDGET (self), NULL); + self->secondary_info = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + self->revealer = g_object_new (GTK_TYPE_REVEALER, + "child", self->secondary_info, + "transition-type", GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN, + NULL); + gtk_widget_insert_after (self->revealer, GTK_WIDGET (self), self->reveal_button); + g_signal_connect (self->reveal_button, "clicked", G_CALLBACK (on_reveal_button_clicked), self); + self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); +} + +static GtkWidget* +_gcr_certificate_widget_add_section (GcrCertificateWidget *self, + const gchar *title, + GIcon *icon, + gboolean important) +{ + GtkWidget *widget; + + g_assert (GCR_IS_CERTIFICATE_WIDGET (self)); + + if (icon) + widget = gcr_section_new_with_icon (title, icon); + else + widget = gcr_section_new (title); + + gtk_size_group_add_widget (self->size_group, widget); + if (important) + gtk_widget_insert_before (widget, GTK_WIDGET (self), self->reveal_button); + else + gtk_box_append (GTK_BOX (self->secondary_info), widget); + + return widget; +} + +static GtkWidget* +create_value_label (const gchar *label) +{ + return g_object_new (GTK_TYPE_LABEL, + "label", label, + "xalign", 1.0, + "halign", GTK_ALIGN_END, + "hexpand", TRUE, + "selectable", TRUE, + "wrap", TRUE, + NULL); +} + +static gchar* +calculate_label (GcrCertificateWidget *self) +{ + gchar *label; + + label = gcr_certificate_get_subject_cn (self->certificate); + if (label != NULL) + return label; + + return g_strdup (_("Certificate")); +} + +static void +on_parsed_dn_part (guint index, + GQuark oid, + GNode *value, + gpointer user_data) +{ + GtkWidget *section = user_data; + const gchar *attr; + const gchar *desc; + gchar *field = NULL; + gchar *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) + field = g_strdup (attr); + else + field = g_strdup_printf ("%s (%s)", attr, desc); + } else if (!attr && !desc) { + field = g_strdup (""); + } else if (attr) { + field = g_strdup (attr); + } else if (desc) { + field = g_strdup (desc); + } else { + g_assert_not_reached (); + } + + display = egg_dn_print_value (oid, value); + if (display == NULL) + display = g_strdup (""); + + gcr_section_add_child (GCR_SECTION (section), field, create_value_label (display)); + + g_free (field); + g_free (display); +} + +static inline gchar * +hex_encode_bytes (GBytes *bytes) +{ + gsize size; + gconstpointer data = g_bytes_get_data (bytes, &size); + return egg_hex_encode_full (data, size, TRUE, " ", 1); +} + +static void +append_subject_public_key (GcrCertificateWidget *self, + GcrSection *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->certificate); + + oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (subject_public_key, + "algorithm", "algorithm", NULL)); + text = egg_oid_get_description (oid); + gcr_section_add_child (section, _("Key Algorithm"), create_value_label (text)); + + value = egg_asn1x_get_element_raw (egg_asn1x_node (subject_public_key, + "algorithm", "parameters", NULL)); + if (value) { + display = hex_encode_bytes (value); + gcr_section_add_child (section, _("Key Parameters"), create_value_label (display)); + g_clear_pointer (&display, g_free); + g_bytes_unref (value); + } + + if (key_nbits > 0) { + display = g_strdup_printf ("%u", key_nbits); + gcr_section_add_child (section, _("Key Size"), create_value_label (display)); + g_clear_pointer (&display, g_free); + } + + 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_bytes_unref (value); + display = egg_hex_encode_full (raw, n_raw, TRUE, " ", 1); + g_free (raw); + gcr_section_add_child (section, _("Key SHA1 Fingerprint"), create_value_label (text)); + g_clear_pointer (&display, g_free); + + value = egg_asn1x_get_bits_as_raw (egg_asn1x_node (subject_public_key, "subjectPublicKey", NULL), &bits); + display = egg_hex_encode_full (g_bytes_get_data (value, NULL), bits / 8, TRUE, " ", 1); + gcr_section_add_child (section, _("Public Key"), create_value_label (text)); + g_clear_pointer (&display, g_free); + g_bytes_unref (value); +} + +static GcrSection * +append_extension_basic_constraints (GcrCertificateWidget *self, + GBytes *data) +{ + GcrSection *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_SECTION (_gcr_certificate_widget_add_section (self, _("Basic Constraints"), NULL, FALSE)); + gcr_section_add_child (section, _("Certificate Authority"), create_value_label (is_ca ? _("Yes") : _("No"))); + + number = g_strdup_printf ("%d", path_len); + gcr_section_add_child (section, _("Max Path Length"), create_value_label (path_len < 0 ? _("Unlimited") : number)); + g_free (number); + + return section; +} + +static GcrSection * +append_extension_extended_key_usage (GcrCertificateWidget *self, + GBytes *data) +{ + GcrSection *section; + GQuark *oids; + GString *text; + guint i; + + oids = _gcr_certificate_extension_extended_key_usage (data); + if (oids == NULL) + return NULL; + + text = g_string_new (""); + for (i = 0; oids[i] != 0; i++) { + if (i > 0) + g_string_append_unichar (text, '\n'); + g_string_append (text, egg_oid_get_description (oids[i])); + } + + g_free (oids); + + section = GCR_SECTION (_gcr_certificate_widget_add_section (self, _("Extended Key Usage"), NULL, FALSE)); + gcr_section_add_child (section, _("Allowed Purposes"), create_value_label (text->str)); + + g_string_free (text, TRUE); + + return section; +} + +static GcrSection * +append_extension_subject_key_identifier (GcrCertificateWidget *self, + GBytes *data) +{ + GcrSection *section; + gpointer keyid; + gsize n_keyid; + + keyid = _gcr_certificate_extension_subject_key_identifier (data, &n_keyid); + if (keyid == NULL) + return NULL; + + section = GCR_SECTION (_gcr_certificate_widget_add_section (self, _("Subject Key Identifier"), NULL, FALSE)); + gchar *display = egg_hex_encode_full (keyid, n_keyid, TRUE, " ", 1); + g_free (keyid); + gcr_section_add_child (section, _("Key Identifier"), create_value_label (display)); + g_free (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 GcrSection * +append_extension_key_usage (GcrCertificateWidget *self, + GBytes *data) +{ + GcrSection *section; + gulong key_usage; + GString *text; + guint i; + + if (!_gcr_certificate_extension_key_usage (data, &key_usage)) + return NULL; + + text = g_string_new (""); + + for (i = 0; i < G_N_ELEMENTS (usage_descriptions); i++) { + if (key_usage & usage_descriptions[i].usage) { + if (text->len > 0) + g_string_append_unichar (text, '\n'); + g_string_append (text, _(usage_descriptions[i].description)); + } + } + + section = GCR_SECTION (_gcr_certificate_widget_add_section (self, _("Key Usage"), NULL, FALSE)); + gcr_section_add_child (section, _("Usages"), create_value_label (text->str)); + + g_string_free (text, TRUE); + + return section; +} + +static GcrSection * +append_extension_subject_alt_name (GcrCertificateWidget *self, + GBytes *data) +{ + GcrSection *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_SECTION (_gcr_certificate_widget_add_section (self, _("Subject Alternative Names"), NULL, FALSE)); + + for (i = 0; i < general_names->len; i++) { + general = &g_array_index (general_names, GcrGeneralName, i); + if (general->display == NULL) { + gchar *display = hex_encode_bytes (general->raw); + gcr_section_add_child (section, general->description, create_value_label (display)); + g_free (display); + } else + gcr_section_add_child (section, general->description, create_value_label (general->display)); + } + + _gcr_general_names_free (general_names); + + return section; +} + +static GcrSection * +append_extension_hex (GcrCertificateWidget *self, + GQuark oid, + GBytes *value) +{ + GcrSection *section; + const gchar *text; + gchar *display; + + section = GCR_SECTION (_gcr_certificate_widget_add_section (self, _("Extension"), NULL, FALSE)); + + /* Extension type */ + text = egg_oid_get_description (oid); + gcr_section_add_child (section, _("Identifier"), create_value_label (text)); + display = hex_encode_bytes (value); + gcr_section_add_child (section, _("Value"), create_value_label (display)); + g_free (display); + + return section; +} + +static void +append_extension (GcrCertificateWidget *self, + GNode *node) +{ + GQuark oid; + GBytes *value; + gboolean critical; + GcrSection *section = NULL; + + /* Dig out the OID */ + oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "extnID", NULL)); + g_return_if_fail (oid); + + /* 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 (self, value); + else if (oid == GCR_OID_EXTENDED_KEY_USAGE) + section = append_extension_extended_key_usage (self, value); + else if (oid == GCR_OID_SUBJECT_KEY_IDENTIFIER) + section = append_extension_subject_key_identifier (self, value); + else if (oid == GCR_OID_KEY_USAGE) + section = append_extension_key_usage (self, value); + else if (oid == GCR_OID_SUBJECT_ALT_NAME) + section = append_extension_subject_alt_name (self, value); + + /* Otherwise the default raw display */ + if (!section) { + section = append_extension_hex (self, oid, value); + } + + /* Critical */ + if (section && egg_asn1x_get_boolean (egg_asn1x_node (node, "critical", NULL), &critical)) { + gcr_section_add_child (section, _("Critical"), create_value_label (critical ? _("Yes") : _("No"))); + } + + g_bytes_unref (value); +} + +/** + * gcr_certificate_widget_new: + * @certificate: (nullable): certificate to display, or %NULL + * + * Create a new certificate widget which displays a given certificate. + * + * Returns: (transfer full): a newly allocated #GcrCertificateWidget, which + * should be freed with g_object_unref() + */ +GtkWidget * +gcr_certificate_widget_new (GcrCertificate *certificate) +{ + return g_object_new (GCR_TYPE_CERTIFICATE_WIDGET, "certificate", certificate, NULL); +} + +/** + * gcr_certificate_widget_get_certificate: + * @self: The certificate widget + * + * Get the certificate displayed in the widget. + * + * Returns: (nullable) (transfer none): the certificate + */ +GcrCertificate * +gcr_certificate_widget_get_certificate (GcrCertificateWidget *self) +{ + g_return_val_if_fail (GCR_IS_CERTIFICATE_WIDGET (self), NULL); + return self->certificate; +} + +/** + * gcr_certificate_widget_set_certificate: + * @self: The certificate widget + * @certificate: (nullable): the certificate to display + * + * Set the certificate displayed in the widget + */ +void +gcr_certificate_widget_set_certificate (GcrCertificateWidget *self, GcrCertificate *certificate) +{ + GtkWidget *section, *label; + PangoAttrList *attributes; + gchar *display; + GIcon *icon; + GBytes *bytes, *number; + GNode *asn, *subject_public_key; + GQuark oid; + gconstpointer data; + gsize n_data; + GDate date; + gulong version; + guint bits, index; + + g_return_if_fail (GCR_IS_CERTIFICATE_WIDGET (self)); + + g_set_object (&self->certificate, certificate); + + data = gcr_certificate_get_der_data (self->certificate, &n_data); + if (!data) { + g_set_object (&self->certificate, NULL); + } + + icon = gcr_certificate_get_icon (self->certificate); + display = calculate_label (self); + section = _gcr_certificate_widget_add_section (self, display, icon, TRUE); + g_clear_pointer (&display, g_free); + g_object_unref (icon); + + bytes = g_bytes_new_static (data, n_data); + asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", bytes); + g_bytes_unref (bytes); + + display = egg_dn_read_part (egg_asn1x_node (asn, "tbsCertificate", "subject", "rdnSequence", NULL), "CN"); + gcr_section_add_child (GCR_SECTION (section), _("Identity"), create_value_label (display)); + g_clear_pointer (&display, g_free); + + display = egg_dn_read_part (egg_asn1x_node (asn, "tbsCertificate", "issuer", "rdnSequence", NULL), "CN"); + gcr_section_add_child (GCR_SECTION (section), _("Verified by"), create_value_label (display)); + g_clear_pointer (&display, g_free); + + if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notAfter", NULL), &date)) { + display = g_malloc0 (128); + if (!g_date_strftime (display, 128, "%x", &date)) + g_return_if_reached (); + gcr_section_add_child (GCR_SECTION (section), _("Expires"), create_value_label (display)); + g_clear_pointer (&display, g_free); + } + + /* The subject */ + section = _gcr_certificate_widget_add_section (self, _("Subject Name"), NULL, FALSE); + egg_dn_parse (egg_asn1x_node (asn, "tbsCertificate", "subject", "rdnSequence", NULL), on_parsed_dn_part, section); + + /* The Issuer */ + section = _gcr_certificate_widget_add_section (self, _("Issuer Name"), NULL, FALSE); + egg_dn_parse (egg_asn1x_node (asn, "tbsCertificate", "issuer", "rdnSequence", NULL), on_parsed_dn_part, section); + + /* The Issued Parameters */ + section = _gcr_certificate_widget_add_section (self, _("Issued Certificate"), NULL, FALSE); + + if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "tbsCertificate", "version", NULL), &version)) { + g_critical ("Unable to parse certificate version"); + } else { + display = g_strdup_printf ("%lu", version + 1); + gcr_section_add_child (GCR_SECTION (section), _("Version"), create_value_label (display)); + g_clear_pointer (&display, g_free); + } + + number = egg_asn1x_get_integer_as_raw (egg_asn1x_node (asn, "tbsCertificate", "serialNumber", NULL)); + if (!number) { + g_critical ("Unable to parse certificate serial number"); + } else { + display = hex_encode_bytes (number); + gcr_section_add_child (GCR_SECTION (section), _("Serial Number"), create_value_label (display)); + g_clear_pointer (&display, g_free); + g_bytes_unref (number); + } + + display = g_malloc0 (128); + if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notBefore", NULL), &date)) { + if (!g_date_strftime (display, 128, "%x", &date)) + g_return_if_reached (); + gcr_section_add_child (GCR_SECTION (section), _("Not Valid Before"), create_value_label (display)); + } + if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notAfter", NULL), &date)) { + if (!g_date_strftime (display, 128, "%x", &date)) + g_return_if_reached (); + gcr_section_add_child (GCR_SECTION (section), _("Not Valid After"), create_value_label (display)); + } + g_clear_pointer (&display, g_free); + + /* Fingerprints */ + section = _gcr_certificate_widget_add_section (self, _("Certificate Fingerprints"), NULL, FALSE); + display = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, bytes); + gcr_section_add_child (GCR_SECTION (section), "SHA1", create_value_label (display)); + g_clear_pointer (&display, g_free); + display = g_compute_checksum_for_bytes (G_CHECKSUM_MD5, bytes); + gcr_section_add_child (GCR_SECTION (section), "MD5", create_value_label (display)); + g_clear_pointer (&display, g_free); + + /* Public Key Info */ + section = _gcr_certificate_widget_add_section (self, _("Public Key Info"), NULL, FALSE); + subject_public_key = egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo", NULL); + append_subject_public_key (self, GCR_SECTION (section), subject_public_key); + + /* Extensions */ + for (index = 1; TRUE; ++index) { + GNode *extension = egg_asn1x_node (asn, "tbsCertificate", "extensions", index, NULL); + if (extension == NULL) + break; + append_extension (self, extension); + } + + /* Signature */ + section = _gcr_certificate_widget_add_section (self, _("Signature"), NULL, FALSE); + + oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "signatureAlgorithm", "algorithm", NULL)); + gcr_section_add_child (GCR_SECTION (section), _("Signature Algorithm"), create_value_label (egg_oid_get_description (oid))); + + bytes = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "signatureAlgorithm", "parameters", NULL)); + if (bytes) { + display = hex_encode_bytes (bytes); + gcr_section_add_child (GCR_SECTION (section), _("Signature Parameters"), create_value_label (display)); + g_clear_pointer (&display, g_free); + g_bytes_unref (bytes); + } + + bytes = egg_asn1x_get_bits_as_raw (egg_asn1x_node (asn, "signature", NULL), &bits); + display = egg_hex_encode_full (g_bytes_get_data (bytes, NULL), bits / 8, TRUE, " ", 1); + g_bytes_unref (bytes); + label = create_value_label (display); + attributes = pango_attr_list_new (); + pango_attr_list_insert (attributes, pango_attr_family_new ("Monospace")); + gtk_label_set_attributes (GTK_LABEL (label), attributes); + pango_attr_list_unref (attributes); + gcr_section_add_child (GCR_SECTION (section), _("Signature"), label); + g_clear_pointer (&display, g_free); + + egg_asn1x_destroy (asn); +} diff --git a/gcr-gtk4/gcr-certificate-widget.h b/gcr-gtk4/gcr-certificate-widget.h new file mode 100644 index 0000000..fb36733 --- /dev/null +++ b/gcr-gtk4/gcr-certificate-widget.h @@ -0,0 +1,30 @@ +/* + * Copyright 2021 Collabora Ltd. + * Copyright Corentin Noël <corentin.noel@collabora.com> + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __GCR_CERTIFICATE_WIDGET_H__ +#define __GCR_CERTIFICATE_WIDGET_H__ + +#include <gck/gck.h> +#include <gcr/gcr.h> + +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GCR_TYPE_CERTIFICATE_WIDGET gcr_certificate_widget_get_type () +G_DECLARE_FINAL_TYPE (GcrCertificateWidget, gcr_certificate_widget, GCR, CERTIFICATE_WIDGET, GtkWidget) + +GtkWidget *gcr_certificate_widget_new (GcrCertificate *certificate); + +GcrCertificate *gcr_certificate_widget_get_certificate (GcrCertificateWidget *self); + +void gcr_certificate_widget_set_certificate (GcrCertificateWidget *self, + GcrCertificate *certificate); + +G_END_DECLS + +#endif /* __GCR_CERTIFICATE_WIDGET_H__ */ diff --git a/gcr-gtk4/gcr-gtk4.h b/gcr-gtk4/gcr-gtk4.h new file mode 100644 index 0000000..5301ce3 --- /dev/null +++ b/gcr-gtk4/gcr-gtk4.h @@ -0,0 +1,15 @@ +#ifndef __GCR_GTK4_H__ +#define __GCR_GTK4_H__ + +#include <glib.h> + +#include <gcr/gcr.h> + +#define __GCR_GTK4_INSIDE_HEADER__ + +#include <gcr-gtk4/gcr-certificate-widget.h> +#include <gcr-gtk4/gcr-renderer.h> + +#undef __GCR_GTK4_INSIDE_HEADER__ + +#endif /* __GCR_GTK4_H__ */ diff --git a/gcr-gtk4/gcr-renderer.c b/gcr-gtk4/gcr-renderer.c new file mode 100644 index 0000000..ebb4f4c --- /dev/null +++ b/gcr-gtk4/gcr-renderer.c @@ -0,0 +1,16 @@ +/* + * Copyright 2021 Collabora Ltd. + * Copyright Corentin Noël <corentin.noel@collabora.com> + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include <gcr-gtk4/gcr-renderer.h> + +G_DEFINE_INTERFACE (GcrRenderer, gcr_renderer, G_TYPE_OBJECT) + +static void +gcr_renderer_default_init (GcrRendererInterface *iface) +{ + /* add properties and signals to the interface here */ +} + diff --git a/gcr-gtk4/gcr-renderer.h b/gcr-gtk4/gcr-renderer.h new file mode 100644 index 0000000..498dca0 --- /dev/null +++ b/gcr-gtk4/gcr-renderer.h @@ -0,0 +1,25 @@ +/* + * Copyright 2021 Collabora Ltd. + * Copyright Corentin Noël <corentin.noel@collabora.com> + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __GCR_RENDERER_H__ +#define __GCR_RENDERER_H__ + +#include <glib-object.h> +#include <gck/gck.h> + +G_BEGIN_DECLS + +#define GCR_TYPE_RENDERER gcr_renderer_get_type () +G_DECLARE_INTERFACE (GcrRenderer, gcr_renderer, GCK, RENDERER, GObject) + +struct _GcrRendererInterface +{ + GTypeInterface g_iface; +}; + +G_END_DECLS + +#endif /* __GCR_RENDERER_H__ */ diff --git a/gcr-gtk4/gcr-section.c b/gcr-gtk4/gcr-section.c new file mode 100644 index 0000000..f888246 --- /dev/null +++ b/gcr-gtk4/gcr-section.c @@ -0,0 +1,225 @@ +#include "gcr-section.h" + +struct _GcrSection +{ + GtkWidget parent_instance; + + GtkWidget *frame; + GtkWidget *label; + GtkWidget *image; + GtkWidget *listbox; + GtkSizeGroup *size_group; +}; + +G_DEFINE_TYPE (GcrSection, gcr_section, GTK_TYPE_WIDGET) + +enum { + PROP_TITLE = 1, + PROP_ICON, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +static void +gcr_section_dispose (GObject *object) +{ + GcrSection *self = (GcrSection *)object; + + g_clear_object (&self->size_group); + g_clear_pointer (&self->label, gtk_widget_unparent); + g_clear_pointer (&self->image, gtk_widget_unparent); + g_clear_pointer (&self->frame, gtk_widget_unparent); + + G_OBJECT_CLASS (gcr_section_parent_class)->dispose (object); +} + +static void +gcr_section_finalize (GObject *object) +{ + GcrSection *self = (GcrSection *)object; + + G_OBJECT_CLASS (gcr_section_parent_class)->finalize (object); +} + +static void +gcr_section_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcrSection *self = GCR_SECTION (object); + + switch (prop_id) + { + case PROP_TITLE: + g_value_set_string (value, gtk_label_get_label (GTK_LABEL (self->label))); + break; + case PROP_ICON: + g_value_set_object (value, gtk_image_get_gicon (GTK_IMAGE (self->image))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcr_section_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcrSection *self = GCR_SECTION (object); + + switch (prop_id) + { + case PROP_TITLE: + gtk_label_set_label (GTK_LABEL (self->label), g_value_get_string (value)); + break; + case PROP_ICON: + if (!self->image) + { + GtkLayoutManager *layout_manager; + GtkLayoutChild *child; + + self->image = gtk_image_new (); + gtk_widget_insert_after (self->image, GTK_WIDGET (self), self->label); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self)); + + child = gtk_layout_manager_get_layout_child (layout_manager, self->image); + g_object_set (G_OBJECT (child), + "column", 1, + NULL); + + child = gtk_layout_manager_get_layout_child (layout_manager, self->frame); + g_object_set (G_OBJECT (child), + "column-span", 2, + NULL); + } + + gtk_image_set_from_gicon (GTK_IMAGE (self->image), g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcr_section_class_init (GcrSectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gcr_section_dispose; + object_class->finalize = gcr_section_finalize; + object_class->get_property = gcr_section_get_property; + object_class->set_property = gcr_section_set_property; + obj_properties[PROP_TITLE] = + g_param_spec_string ("title", + "Title", + "The title of the section", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_ICON] = + g_param_spec_object ("icon", + "Icon", + "The Icon to use", + G_TYPE_ICON, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, + N_PROPERTIES, + obj_properties); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_GRID_LAYOUT); +} + +static void +gcr_section_init (GcrSection *self) +{ + GtkLayoutManager *layout_manager; + GtkLayoutChild *child; + + self->label = gtk_label_new (NULL); + g_object_set (G_OBJECT (self->label), + "margin-start", 12, + "margin-end", 12, + "margin-top", 8, + "margin-bottom", 8, + "xalign", 0.0, + "halign", GTK_ALIGN_START, + "hexpand", TRUE, + NULL); + gtk_style_context_add_class (gtk_widget_get_style_context (self->label), "caption-heading"); + self->listbox = gtk_list_box_new (); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (self->listbox), GTK_SELECTION_NONE); + self->frame = gtk_frame_new (NULL); + g_object_set (G_OBJECT (self->frame), + "child", self->listbox, + "hexpand", TRUE, + NULL); + self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_widget_insert_after (self->label, GTK_WIDGET (self), NULL); + gtk_widget_insert_after (self->frame, GTK_WIDGET (self), self->label); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self)); + + child = gtk_layout_manager_get_layout_child (layout_manager, self->frame); + g_object_set (G_OBJECT (child), + "row", 1, + NULL); + g_object_set (self, + "margin-start", 12, + "margin-end", 12, + "margin-top", 8, + "margin-bottom", 8, + NULL); +} + +GtkWidget * +gcr_section_new (const gchar *title) +{ + return g_object_new (GCR_TYPE_SECTION, "title", title, NULL); +} + +GtkWidget * +gcr_section_new_with_icon (const gchar *title, + GIcon *icon) +{ + return g_object_new (GCR_TYPE_SECTION, "title", title, "icon", icon, NULL); +} + +void +gcr_section_add_child (GcrSection *self, + const gchar *description, + GtkWidget *child) +{ + GtkWidget *row, *box, *label; + g_return_if_fail (GCR_IS_SECTION (self)); + + row = gtk_list_box_row_new (); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box); + label = gtk_label_new (description); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + g_object_set (label, + "margin-start", 12, + "margin-end", 12, + "margin-top", 8, + "margin-bottom", 8, + "xalign", 0.0, + "halign", GTK_ALIGN_START, + NULL); + gtk_style_context_add_class (gtk_widget_get_style_context (label), "caption"); + g_object_set (child, + "margin-start", 12, + "margin-end", 12, + "margin-top", 8, + "margin-bottom", 8, + "halign", GTK_ALIGN_END, + NULL); + gtk_size_group_add_widget (self->size_group, label); + gtk_box_append (GTK_BOX (box), label); + gtk_box_append (GTK_BOX (box), child); + gtk_list_box_append (GTK_LIST_BOX (self->listbox), row); +} diff --git a/gcr-gtk4/gcr-section.h b/gcr-gtk4/gcr-section.h new file mode 100644 index 0000000..9f275d9 --- /dev/null +++ b/gcr-gtk4/gcr-section.h @@ -0,0 +1,30 @@ +/* + * Copyright 2021 Collabora Ltd. + * Copyright Corentin Noël <corentin.noel@collabora.com> + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __GCR_SECTION_H__ +#define __GCR_SECTION_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GCR_TYPE_SECTION gcr_section_get_type() + +G_DECLARE_FINAL_TYPE (GcrSection, gcr_section, GCR, SECTION, GtkWidget) + +GtkWidget *gcr_section_new (const gchar *title); +GtkWidget *gcr_section_new_with_icon (const gchar *title, + GIcon *icon); + +void gcr_section_add_child (GcrSection *self, + const gchar *description, + GtkWidget *child); + +G_END_DECLS + +#endif /* __GCR_SECTION_H__ */ diff --git a/gcr-gtk4/libgcr-gtk4.map b/gcr-gtk4/libgcr-gtk4.map new file mode 100644 index 0000000..4a16138 --- /dev/null +++ b/gcr-gtk4/libgcr-gtk4.map @@ -0,0 +1,8 @@ +{ +global: + gcr_*; + _gcr_*; + SECMEM_*; +local: + *; +}; diff --git a/gcr-gtk4/meson.build b/gcr-gtk4/meson.build new file mode 100644 index 0000000..e97e9ac --- /dev/null +++ b/gcr-gtk4/meson.build @@ -0,0 +1,135 @@ +gcr_gtk4_headers_install_dir = 'gcr-@0@'.format(gcr_major_version) / 'gcr-gtk4' + +gcr_gtk4_public_sources = files( + 'gcr-renderer.c', + 'gcr-certificate-widget.c', +) + +gcr_gtk4_private_sources = files( + 'gcr-section.c', +) + +gcr_gtk4_headers = files( + 'gcr-renderer.h', + 'gcr-certificate-widget.h', +) + + +gcr_gtk4_enums_gen = gnome.mkenums_simple('gcr-gtk4-enum-types', + sources: gcr_gtk4_headers, + install_header: true, + install_dir: get_option('includedir') / gcr_gtk4_headers_install_dir, +) + +gcr_gtk4_sources = [ + gcr_gtk4_private_sources, + gcr_gtk4_public_sources, + gcr_gtk4_enums_gen, +] + +gcr_gtk4_deps = [ + glib_deps, + p11kit_dep, + libegg_dep, + gck_dep, + gcr_dep, + gtk4_dep, +] + +gcr_gtk4_cflags = [ + '-DG_LOG_DOMAIN="GcrGtk4"', + '-DGCR_COMPILATION', + '-DGCR_API_SUBJECT_TO_CHANGE', + '-DGCK_API_SUBJECT_TO_CHANGE', + '-DP11_KIT_API_SUBJECT_TO_CHANGE', +] + +gcr_gtk4_symbolmap = meson.current_source_dir() / 'libgcr-gtk4.map' +gcr_gtk4_linkflags = cc.get_supported_link_arguments( + '-Wl,--version-script,@0@'.format(gcr_gtk4_symbolmap), +) + +gcr_gtk4_basename = 'gcr-gtk4-@0@'.format(gcr_major_version) + +gcr_gtk4_lib = shared_library(gcr_gtk4_basename, + gcr_gtk4_sources, + dependencies: gcr_gtk4_deps, + c_args: gcr_gtk4_cflags, + link_args: gcr_gtk4_linkflags, + link_depends: gcr_gtk4_symbolmap, + include_directories: config_h_dir, + version: gcr_soversion, + install: true, +) + +gcr_gtk4_pkgconf_deps = [ + glib_dep, + gio_dep, + gobject_dep, + 'gck-@0@'.format(gck_major_version), + 'gcr-@0@'.format(gcr_major_version), + gtk4_dep, +] +pkgconfig.generate(gcr_gtk4_lib, + subdirs: 'gcr-@0@'.format(gcr_major_version), + requires: gcr_gtk4_pkgconf_deps, + description: 'Library providing Gtk4 widgets for high level crypto parsing and display', +) + +install_headers([gcr_gtk4_headers ], + subdir: gcr_gtk4_headers_install_dir, +) + +gcr_gtk4_dep = declare_dependency( + link_with: gcr_gtk4_lib, + sources: gcr_gtk4_enums_gen[1], # Make sure gcr-enum-types.h can be included +) + +if get_option('introspection') + gcr_gtk4_gir = gnome.generate_gir(gcr_gtk4_lib, + sources: [ gcr_gtk4_headers, gcr_gtk4_public_sources ], + namespace: 'GcrGtk4', + nsversion: '@0@'.format(gcr_major_version), + export_packages: gcr_gtk4_basename, + identifier_prefix: 'Gcr', + symbol_prefix: 'gcr', + packages: gcr_gtk4_deps, + includes: [ + 'GObject-2.0', + 'Gio-2.0', + 'Gtk-4.0', + gck_gir[0], + gcr_gir[0], + ], + header: 'gcr-gtk4/gcr-gtk4.h', + extra_args: [ + '-DGCR_COMPILATION', + '-DGCR_API_SUBJECT_TO_CHANGE', + ], + install: true, + ) + + gcr_gtk4_vapi = gnome.generate_vapi(gcr_gtk4_basename, + sources: gcr_gtk4_gir[0], + packages: [ + 'glib-2.0', + 'gio-2.0', + gck_vapi, + gcr_vapi, + 'gtk4' + ], + metadata_dirs: meson.current_source_dir(), + vapi_dirs: [ + build_root / 'gck', + build_root / 'gcr', + ], + gir_dirs: [ + build_root / 'gck', + build_root / 'gcr', + ], + install: true, + ) +endif + +# gcr-viewer-gtk4 +subdir('viewer') diff --git a/gcr-gtk4/viewer/meson.build b/gcr-gtk4/viewer/meson.build new file mode 100644 index 0000000..c33fae9 --- /dev/null +++ b/gcr-gtk4/viewer/meson.build @@ -0,0 +1,7 @@ +gcr_viewer = executable('gcr-viewer-gtk4', + 'viewer.c', + dependencies: [ gcr_gtk4_dep, gcr_gtk4_deps ], + c_args: gcr_gtk4_cflags, + include_directories: config_h_dir, + install: true, +) diff --git a/gcr-gtk4/viewer/viewer.c b/gcr-gtk4/viewer/viewer.c new file mode 100644 index 0000000..b08ebe7 --- /dev/null +++ b/gcr-gtk4/viewer/viewer.c @@ -0,0 +1,93 @@ +/* + * Copyright 2011,2021 Collabora Ltd. + * Copyright Corentin Noël <corentin.noel@collabora.com> + * Copyright Stef Walter <stefw@collabora.co.uk> + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gcr-gtk4/gcr-gtk4.h> + +#include "config.h" + +static gchar **remaining_args = NULL; + +static gboolean +print_version_and_exit (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_print("%s -- %s\n", _("GCR Certificate and Key Viewer"), VERSION); + exit (0); + return TRUE; +} + +static const GOptionEntry options[] = { + { "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + print_version_and_exit, N_("Show the application's version"), NULL}, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, + &remaining_args, NULL, N_("[file...]") }, + { NULL } +}; + +static void +activate (GtkApplication* app, + gpointer user_data) +{ + GtkWidget *window; + GtkWidget *box; + GtkWidget *scrolled; + GCancellable *cancellable = NULL; + + window = gtk_application_window_new (app); + gtk_window_set_title (GTK_WINDOW (window), "Window"); + gtk_window_set_default_size (GTK_WINDOW (window), 400, 300); + scrolled = gtk_scrolled_window_new (); + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + g_object_set (G_OBJECT (scrolled), + "child", box, + "hscrollbar-policy", GTK_POLICY_NEVER, + NULL); + + if (remaining_args) { + for (int i = 0; remaining_args[i] != NULL; ++i) { + GFile *file; + GError *error = NULL; + GcrCertificate *certificate; + GtkWidget *widget; + + file = g_file_new_for_commandline_arg (remaining_args[i]); + certificate = gcr_simple_certificate_new_from_file (file, cancellable, &error); + g_object_unref (file); + widget = gcr_certificate_widget_new (GCR_CERTIFICATE (certificate)); + g_object_unref (certificate); + gtk_widget_set_halign (widget, GTK_ALIGN_CENTER); + gtk_box_append (GTK_BOX (box), widget); + } + + g_clear_pointer (&remaining_args, g_strfreev); + remaining_args = NULL; + } + + gtk_window_set_child (GTK_WINDOW (window), scrolled); + gtk_widget_show (window); +} + +int +main (int argc, + char **argv) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("org.gnome.GcrViewerGtk4", G_APPLICATION_FLAGS_NONE); + g_application_add_main_option_entries (G_APPLICATION (app), options); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} diff --git a/gcr/frob-parser.c b/gcr/frob-parser.c index f071eb5..452bf43 100644 --- a/gcr/frob-parser.c +++ b/gcr/frob-parser.c @@ -43,7 +43,7 @@ dump_certificate (GckAttributes *attrs, const gchar *filename) if (!attr) return FALSE; - cert = gcr_simple_certificate_new_static (attr->value, attr->length); + cert = gcr_simple_certificate_new (g_bytes_new_static (attr->value, attr->length)); subject = gcr_certificate_get_subject_dn (cert); g_print ("%s: parsed certificate: %s\n", filename, subject); g_free (subject); diff --git a/gcr/gcr-certificate-chain.c b/gcr/gcr-certificate-chain.c index 1793869..262ed07 100644 --- a/gcr/gcr-certificate-chain.c +++ b/gcr/gcr-certificate-chain.c @@ -173,9 +173,11 @@ prep_chain_private_thread_safe (GcrCertificateChainPrivate *orig, const gchar *p /* Otherwise copy the certificate data */ } else { + GBytes *bytes; der = gcr_certificate_get_der_data (certificate, &n_der); g_return_val_if_fail (der, NULL); - safe = gcr_simple_certificate_new (der, n_der); + bytes = g_bytes_new (der, n_der); + safe = gcr_simple_certificate_new (bytes); g_debug ("copying certificate so it's thread safe"); diff --git a/gcr/gcr-simple-certificate.c b/gcr/gcr-simple-certificate.c index 61b9180..0862134 100644 --- a/gcr/gcr-simple-certificate.c +++ b/gcr/gcr-simple-certificate.c @@ -23,6 +23,7 @@ #include "gcr-comparable.h" #include "gcr-internal.h" #include "gcr-simple-certificate.h" +#include "gcr-parser.h" #include <string.h> @@ -124,51 +125,86 @@ gcr_simple_certificate_iface_init (GcrCertificateIface *iface) /** * gcr_simple_certificate_new: - * @data: (array length=n_data): the raw DER certificate data - * @n_data: The length of @data + * @bytes: (transfer full): a #GBytes containing the raw DER certificate data * - * Create a new #GcrSimpleCertificate for the raw DER data. The @data memory is - * copied so you can dispose of it after this function returns. + * Create a new #GcrSimpleCertificate for the raw DER data. The @bytes memory is + * then owned by the returned object, use g_bytes_ref() before calling this + * function if you need to keep the #GBytes. * * Returns: (transfer full) (type Gcr.SimpleCertificate): a new #GcrSimpleCertificate */ GcrCertificate * -gcr_simple_certificate_new (const guchar *data, - gsize n_data) +gcr_simple_certificate_new (GBytes *bytes) { GcrSimpleCertificate *cert; - g_return_val_if_fail (data, NULL); - g_return_val_if_fail (n_data, NULL); + g_return_val_if_fail (bytes != NULL, NULL); cert = g_object_new (GCR_TYPE_SIMPLE_CERTIFICATE, NULL); + cert->bytes = bytes; - cert->bytes = g_bytes_new (data, n_data); return GCR_CERTIFICATE (cert); + +} + +static void +on_parser_parsed (GcrParser *parser, + gpointer user_data) +{ + GcrSimpleCertificate *self = user_data; + GckAttributes *attributes; + const GckAttribute *attr; + + attributes = gcr_parser_get_parsed_attributes (parser); + attr = gck_attributes_find (attributes, CKA_VALUE); + self->bytes = g_bytes_new (attr->value, attr->length); } -/** - * gcr_simple_certificate_new_static: (skip) - * @data: (array length=n_data): The raw DER certificate data - * @n_data: The length of @data - * - * Create a new #GcrSimpleCertificate for the raw DER data. The @data memory is - * not copied and must persist until the #GcrSimpleCertificate object is - * destroyed. - * - * Returns: (transfer full) (type Gcr.SimpleCertificate): a new #GcrSimpleCertificate - */ GcrCertificate * -gcr_simple_certificate_new_static (const guchar *data, - gsize n_data) +gcr_simple_certificate_new_from_file (GFile *file, + GCancellable *cancellable, + GError **error) { GcrSimpleCertificate *cert; + GcrParser *parser; + GBytes *bytes; - g_return_val_if_fail (data, NULL); - g_return_val_if_fail (n_data, NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (!error || !*error, NULL); - cert = g_object_new (GCR_TYPE_SIMPLE_CERTIFICATE, NULL); - cert->bytes = g_bytes_new_static (data, n_data); + bytes = g_file_load_bytes (file, cancellable, NULL, error); + if (!bytes) { + return NULL; + } + + parser = gcr_parser_new (); + cert = g_object_new (GCR_TYPE_SIMPLE_CERTIFICATE, NULL); + g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), cert); + if (!gcr_parser_parse_bytes (parser, bytes, error)) { + g_bytes_unref (bytes); + g_object_unref (parser); + g_object_unref (cert); + return NULL; + } + + g_bytes_unref (bytes); + g_object_unref (parser); return GCR_CERTIFICATE (cert); } + +void +gcr_simple_certificate_new_from_file_async (GFile *file, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + +} + +GcrCertificate * +gcr_simple_certificate_new_from_file_finish (GAsyncResult *res, + GError **error) +{ + return NULL; +} diff --git a/gcr/gcr-simple-certificate.h b/gcr/gcr-simple-certificate.h index 9ad042f..b82e9dd 100644 --- a/gcr/gcr-simple-certificate.h +++ b/gcr/gcr-simple-certificate.h @@ -25,6 +25,7 @@ #define __GCR_SIMPLE_CERTIFICATE_H__ #include <glib-object.h> +#include <gio/gio.h> #include "gcr-certificate.h" @@ -34,11 +35,16 @@ G_BEGIN_DECLS #define GCR_TYPE_SIMPLE_CERTIFICATE gcr_simple_certificate_get_type () G_DECLARE_FINAL_TYPE (GcrSimpleCertificate, gcr_simple_certificate, GCR, SIMPLE_CERTIFICATE, GObject) -GcrCertificate *gcr_simple_certificate_new (const guchar *data, - gsize n_data); - -GcrCertificate *gcr_simple_certificate_new_static (const guchar *data, - gsize n_data); +GcrCertificate *gcr_simple_certificate_new (GBytes *bytes); +GcrCertificate *gcr_simple_certificate_new_from_file (GFile *file, + GCancellable *cancellable, + GError **error); +void gcr_simple_certificate_new_from_file_async (GFile *file, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GcrCertificate *gcr_simple_certificate_new_from_file_finish (GAsyncResult *res, + GError **error); G_END_DECLS @@ -38,6 +38,7 @@ #include <gcr/gcr-column.h> #include <gcr/gcr-enum-types-base.h> #include <gcr/gcr-filter-collection.h> +#include <gcr/gcr-fingerprint.h> #include <gcr/gcr-icons.h> #include <gcr/gcr-importer.h> #include <gcr/gcr-library.h> diff --git a/gcr/test-certificate-chain.c b/gcr/test-certificate-chain.c index 06347e5..ba4157d 100644 --- a/gcr/test-certificate-chain.c +++ b/gcr/test-certificate-chain.c @@ -167,8 +167,7 @@ setup (Test *test, gconstpointer unused) /* A self-signed certificate */ if (!g_file_get_contents (SRCDIR "/gcr/fixtures/der-certificate.crt", &contents, &n_contents, NULL)) g_assert_not_reached (); - test->cert_self = gcr_simple_certificate_new ((const guchar *)contents, n_contents); - g_free (contents); + test->cert_self = gcr_simple_certificate_new (g_bytes_new_take (contents, n_contents)); /* A signed certificate */ if (!g_file_get_contents (SRCDIR "/gcr/fixtures/dhansak-collabora.cer", &contents, &n_contents, NULL)) diff --git a/gcr/test-certificate.c b/gcr/test-certificate.c index 2fed913..b3e4259 100644 --- a/gcr/test-certificate.c +++ b/gcr/test-certificate.c @@ -41,26 +41,22 @@ typedef struct { static void setup (Test *test, gconstpointer unused) { - gchar *contents; - gsize n_contents; + GFile *file; - if (!g_file_get_contents (SRCDIR "/gcr/fixtures/der-certificate.crt", &contents, &n_contents, NULL)) - g_assert_not_reached (); - test->certificate = gcr_simple_certificate_new ((const guchar *)contents, n_contents); + file = g_file_new_for_path (SRCDIR "/gcr/fixtures/der-certificate.crt"); + test->certificate = gcr_simple_certificate_new_from_file (file, NULL, NULL); + g_object_unref (file); g_assert (test->certificate); - g_free (contents); - if (!g_file_get_contents (SRCDIR "/gcr/fixtures/der-certificate-dsa.cer", &contents, &n_contents, NULL)) - g_assert_not_reached (); - test->dsa_cert = gcr_simple_certificate_new ((const guchar *)contents, n_contents); + file = g_file_new_for_path (SRCDIR "/gcr/fixtures/der-certificate-dsa.cer"); + test->dsa_cert = gcr_simple_certificate_new_from_file (file, NULL, NULL); + g_object_unref (file); g_assert (test->dsa_cert); - g_free (contents); - if (!g_file_get_contents (SRCDIR "/gcr/fixtures/dhansak-collabora.cer", &contents, &n_contents, NULL)) - g_assert_not_reached (); - test->dhansak_cert = gcr_simple_certificate_new ((const guchar *)contents, n_contents); + file = g_file_new_for_path (SRCDIR "/gcr/fixtures/dhansak-collabora.cer"); + test->dhansak_cert = gcr_simple_certificate_new_from_file (file, NULL, NULL); + g_object_unref (file); g_assert (test->dhansak_cert); - g_free (contents); } static void diff --git a/gcr/test-pkcs11-certificate.c b/gcr/test-pkcs11-certificate.c index 53003e6..9b70561 100644 --- a/gcr/test-pkcs11-certificate.c +++ b/gcr/test-pkcs11-certificate.c @@ -125,7 +125,7 @@ test_lookup_certificate_issuer (Test *test, gconstpointer unused) gconstpointer der; gsize n_der; - cert = gcr_simple_certificate_new_static (test->cert_data, test->n_cert_data); + cert = gcr_simple_certificate_new (g_bytes_new_static (test->cert_data, test->n_cert_data)); g_assert (cert); /* Should be self-signed, so should find itself (added in setup) */ @@ -166,7 +166,7 @@ test_lookup_certificate_issuer_not_found (Test *test, gconstpointer unused) GcrCertificate *cert, *issuer; GError *error = NULL; - cert = gcr_simple_certificate_new_static (test->cert2_data, test->n_cert2_data); + cert = gcr_simple_certificate_new (g_bytes_new_static (test->cert2_data, test->n_cert2_data)); g_assert (cert); /* Issuer shouldn't be found */ @@ -194,7 +194,7 @@ test_lookup_certificate_issuer_async (Test *test, gconstpointer unused) gconstpointer der; gsize n_der; - cert = gcr_simple_certificate_new_static (test->cert_data, test->n_cert_data); + cert = gcr_simple_certificate_new (g_bytes_new_static (test->cert_data, test->n_cert_data)); g_assert (cert); /* Should be self-signed, so should find itself (added in setup) */ @@ -222,7 +222,7 @@ test_lookup_certificate_issuer_failure (Test *test, gconstpointer unused) GcrCertificate *cert, *issuer; GError *error = NULL; - cert = gcr_simple_certificate_new_static (test->cert_data, test->n_cert_data); + cert = gcr_simple_certificate_new (g_bytes_new_static (test->cert_data, test->n_cert_data)); g_assert (cert); /* Make the lookup fail */ @@ -244,7 +244,7 @@ test_lookup_certificate_issuer_fail_async (Test *test, gconstpointer unused) GcrCertificate *cert, *issuer; GError *error = NULL; - cert = gcr_simple_certificate_new_static (test->cert_data, test->n_cert_data); + cert = gcr_simple_certificate_new (g_bytes_new_static (test->cert_data, test->n_cert_data)); g_assert (cert); /* Make the lookup fail */ diff --git a/gcr/test-simple-certificate.c b/gcr/test-simple-certificate.c index 945768e..d47bdbc 100644 --- a/gcr/test-simple-certificate.c +++ b/gcr/test-simple-certificate.c @@ -33,23 +33,25 @@ #include <errno.h> typedef struct { - gpointer cert_data; - gsize n_cert_data; + GBytes *bytes; } Test; static void setup (Test *test, gconstpointer unused) { - if (!g_file_get_contents (SRCDIR "/gcr/fixtures/der-certificate.crt", (gchar**)&test->cert_data, - &test->n_cert_data, NULL)) + gchar *contents; + gsize length; + if (!g_file_get_contents (SRCDIR "/gcr/fixtures/der-certificate.crt", &contents, &length, NULL)) g_assert_not_reached (); - g_assert (test->cert_data); + + test->bytes = g_bytes_new_take (contents, length); + g_assert (test->bytes); } static void teardown (Test *test, gconstpointer unused) { - g_free (test->cert_data); + g_clear_pointer (&test->bytes, g_bytes_unref); } static void @@ -59,30 +61,34 @@ test_new (Test *test, gconstpointer unused) gconstpointer der; gsize n_der; - cert = gcr_simple_certificate_new (test->cert_data, test->n_cert_data); + cert = gcr_simple_certificate_new (test->bytes); g_assert (GCR_IS_SIMPLE_CERTIFICATE (cert)); der = gcr_certificate_get_der_data (cert, &n_der); g_assert (der); - egg_assert_cmpmem (der, n_der, ==, test->cert_data, test->n_cert_data); g_object_unref (cert); } static void -test_new_static (Test *test, gconstpointer unused) +test_new_from_file (Test *test, gconstpointer unused) { GcrCertificate *cert; + GFile *file; gconstpointer der; gsize n_der; + gconstpointer ref_der; + gsize ref_n_der; - cert = gcr_simple_certificate_new_static (test->cert_data, test->n_cert_data); + file = g_file_new_for_path (SRCDIR "/gcr/fixtures/der-certificate.crt"); + cert = gcr_simple_certificate_new_from_file (file, NULL, NULL); g_assert (GCR_IS_SIMPLE_CERTIFICATE (cert)); der = gcr_certificate_get_der_data (cert, &n_der); g_assert (der); - egg_assert_cmpsize (n_der, ==, test->n_cert_data); - g_assert (der == test->cert_data); /* Must be same pointer */ + ref_der = g_bytes_get_data (test->bytes, &ref_n_der); + egg_assert_cmpsize (n_der, ==, ref_n_der); + egg_assert_cmpmem (der, n_der, ==, ref_der, ref_n_der); g_object_unref (cert); } @@ -94,7 +100,7 @@ main (int argc, char **argv) g_set_prgname ("test-simple-certificate"); g_test_add ("/gcr/simple-certificate/new", Test, NULL, setup, test_new, teardown); - g_test_add ("/gcr/simple-certificate/new_static", Test, NULL, setup, test_new_static, teardown); + g_test_add ("/gcr/simple-certificate/new_from_file", Test, NULL, setup, test_new_from_file, teardown); return g_test_run (); } diff --git a/gcr/test-trust.c b/gcr/test-trust.c index 0af02f7..1999167 100644 --- a/gcr/test-trust.c +++ b/gcr/test-trust.c @@ -56,8 +56,7 @@ setup (Test *test, gconstpointer unused) g_assert_not_reached (); g_assert (contents); - test->certificate = gcr_simple_certificate_new ((const guchar *)contents, len); - g_free (contents); + test->certificate = gcr_simple_certificate_new (g_bytes_new_take (contents, len)); rv = gck_mock_C_GetFunctionList (&f); gck_assert_cmprv (rv, ==, CKR_OK); diff --git a/meson.build b/meson.build index 6fa0ff4..9227148 100644 --- a/meson.build +++ b/meson.build @@ -70,6 +70,10 @@ if get_option('gtk') gtk_dep = dependency('gtk+-3.0', version: '>=' + gtk_min_version) endif +if get_option('gtk4') + gtk4_dep = dependency('gtk4') +endif + # configuration conf = configuration_data() conf.set_quoted('VERSION', meson.project_version()) @@ -100,6 +104,9 @@ subdir('schema') if get_option('gtk') subdir('ui') endif +if get_option('gtk4') + subdir('gcr-gtk4') +endif if get_option('gtk_doc') subdir('docs') endif diff --git a/meson_options.txt b/meson_options.txt index f96a47c..fbc76f8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,6 +8,11 @@ option('gtk', value: true, description: 'Build code that uses GTK+', ) +option('gtk4', + type: 'boolean', + value: true, + description: 'Build the Gtk4 library', +) option('gtk_doc', type: 'boolean', value: true, diff --git a/ui/gcr-certificate-renderer.c b/ui/gcr-certificate-renderer.c index 65be5ba..ca5bcff 100644 --- a/ui/gcr-certificate-renderer.c +++ b/ui/gcr-certificate-renderer.c @@ -339,9 +339,7 @@ gcr_certificate_renderer_dispose (GObject *obj) { GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (obj); - if (self->pv->opt_cert) - g_object_unref (self->pv->opt_cert); - self->pv->opt_cert = NULL; + g_clear_object (&self->pv->opt_cert); G_OBJECT_CLASS (gcr_certificate_renderer_parent_class)->dispose (obj); } @@ -351,14 +349,8 @@ gcr_certificate_renderer_finalize (GObject *obj) { GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (obj); - g_assert (!self->pv->opt_cert); - - if (self->pv->opt_attrs) - gck_attributes_unref (self->pv->opt_attrs); - self->pv->opt_attrs = NULL; - - g_free (self->pv->label); - self->pv->label = NULL; + g_clear_pointer (&self->pv->opt_attrs, gck_attributes_unref); + g_clear_pointer (&self->pv->label, g_free); G_OBJECT_CLASS (gcr_certificate_renderer_parent_class)->finalize (obj); } diff --git a/ui/gcr-viewer-tool.c b/ui/gcr-viewer-tool.c index 0117d70..e0a743b 100644 --- a/ui/gcr-viewer-tool.c +++ b/ui/gcr-viewer-tool.c @@ -124,3 +124,4 @@ main (int argc, char *argv[]) return 0; } + |