/*
* gnome-keyring
*
* Copyright (C) 2008 Stefan Walter
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
#include "config.h"
#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"
#include "gcr/gcr-oids.h"
#include "egg/egg-asn1x.h"
#include "egg/egg-asn1-defs.h"
#include "egg/egg-dn.h"
#include "egg/egg-hex.h"
#include "egg/egg-oid.h"
#include
#include
/**
* GcrCertificate:
*
* An interface that represents an X.509 certificate.
*
* Objects can implement this interface to make a certificate usable with the
* GCR library.
*
* Various methods are available to parse out relevant bits of the certificate.
* However no verification of the validity of a certificate is done here. Use
* your favorite crypto library to do this.
*
* You can use [class@SimpleCertificate] to simply load a certificate for which
* you already have the raw certificate data.
*
* The #GcrCertificate interface has several properties that must be implemented.
* You can use a mixin to implement these properties if desired. See the
* gcr_certificate_mixin_class_init() and gcr_certificate_mixin_get_property()
* functions.
*/
/**
* GcrCertificateIface:
* @parent: the parent interface type
* @get_der_data: a method which returns the RAW der data of the certificate
*
* The interface that implementors of #GcrCertificate must implement.
*/
/*
* The DER data in this structure is owned by the derived class.
* It is only valid for the duration of the current call stack
* after we call gcr_certificate_get_der_data(). We shouldn't
* save it anywhere else.
*
* We keep the pointer around and compare it so that if the derived
* class returns exactly the same pointer and size, then we can
* keep from parsing things over again.
*/
typedef struct _GcrCertificateInfo {
gconstpointer der;
gsize n_der;
GNode *asn1;
guint key_size;
} GcrCertificateInfo;
/* Forward declarations */
static GBytes * _gcr_certificate_get_subject_const (GcrCertificate *self);
static GBytes * _gcr_certificate_get_issuer_const (GcrCertificate *self);
enum {
PROP_FIRST = 0x0007000,
PROP_LABEL,
PROP_DESCRIPTION,
PROP_SUBJECT_NAME,
PROP_ISSUER_NAME,
PROP_EXPIRY_DATE
};
/* -----------------------------------------------------------------------------
* INTERNAL
*/
static GQuark CERTIFICATE_INFO = 0;
static void
certificate_info_free (gpointer data)
{
GcrCertificateInfo *info = data;
if (info) {
g_assert (info->asn1);
egg_asn1x_destroy (info->asn1);
g_free (info);
}
}
static GcrCertificateInfo*
certificate_info_load (GcrCertificate *cert)
{
GcrCertificateInfo *info;
GBytes *bytes;
GNode *asn1;
gconstpointer der;
gsize n_der;
g_assert (GCR_IS_CERTIFICATE (cert));
der = gcr_certificate_get_der_data (cert, &n_der);
if (der == NULL)
return NULL;
info = g_object_get_qdata (G_OBJECT (cert), CERTIFICATE_INFO);
if (info != NULL) {
if (n_der == info->n_der && der == info->der)
return info;
}
/* TODO: Once GBytes is public, add to GcrCertificate interface */
bytes = g_bytes_new_static (der, n_der);
/* Cache is invalid or non existent */
asn1 = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", bytes);
g_bytes_unref (bytes);
if (asn1 == NULL) {
g_warning ("a derived class provided an invalid or unparseable X.509 DER certificate data.");
return NULL;
}
info = g_new0 (GcrCertificateInfo, 1);
info->der = der;
info->n_der = n_der;
info->asn1 = asn1;
g_object_set_qdata_full (G_OBJECT (cert), CERTIFICATE_INFO, info, certificate_info_free);
return info;
}
static GChecksum*
digest_certificate (GcrCertificate *self, GChecksumType type)
{
GChecksum *digest;
gconstpointer der;
gsize n_der;
g_assert (GCR_IS_CERTIFICATE (self));
der = gcr_certificate_get_der_data (self, &n_der);
if (der == NULL)
return NULL;
digest = g_checksum_new (type);
g_return_val_if_fail (digest, NULL);
g_checksum_update (digest, der, n_der);
return digest;
}
/* ---------------------------------------------------------------------------------
* INTERFACE
*/
static void
gcr_certificate_default_init (GcrCertificateIface *iface)
{
static size_t initialized = 0;
if (g_once_init_enter (&initialized)) {
CERTIFICATE_INFO = g_quark_from_static_string ("_gcr_certificate_certificate_info");
/**
* GcrCertificate:label:
*
* A readable label for this certificate.
*/
g_object_interface_install_property (iface,
g_param_spec_string ("label", "Label", "Certificate label",
"", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GcrCertificate:description:
*
* A readable description for this certificate
*/
g_object_interface_install_property (iface,
g_param_spec_string ("description", "Description", "Description of object being rendered",
"", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GcrCertificate:subject:
*
* Common name part of the certificate subject
*/
g_object_interface_install_property (iface,
g_param_spec_string ("subject-name", "Subject name", "Common name of subject",
"", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GcrCertificate:issuer-name:
*
* Common name part of the certificate issuer
*/
g_object_interface_install_property (iface,
g_param_spec_string ("issuer-name", "Issuer name", "Common name of issuer",
"", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GcrCertificate:expiry-date:
*
* The expiry date of the certificate
*/
g_object_interface_install_property (iface,
g_param_spec_boxed ("expiry-date", "Expiry date", "Certificate expiry date",
G_TYPE_DATE_TIME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_once_init_leave (&initialized, 1);
}
}
typedef GcrCertificateIface GcrCertificateInterface;
G_DEFINE_INTERFACE (GcrCertificate, gcr_certificate, G_TYPE_OBJECT);
/* -----------------------------------------------------------------------------
* PUBLIC
*/
/**
* gcr_certificate_get_der_data: (virtual get_der_data)
* @self: a #GcrCertificate
* @n_data: a pointer to a location to store the size of the resulting DER data.
*
* Gets the raw DER data for an X.509 certificate.
*
* Returns: (transfer none) (array length=n_data): raw DER data of the X.509 certificate
**/
const guint8 *
gcr_certificate_get_der_data (GcrCertificate *self,
gsize *n_data)
{
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (n_data != NULL, NULL);
g_return_val_if_fail (GCR_CERTIFICATE_GET_INTERFACE (self)->get_der_data, NULL);
return GCR_CERTIFICATE_GET_INTERFACE (self)->get_der_data (self, n_data);
}
/**
* gcr_certificate_get_issuer_name:
* @self: a #GcrCertificate
*
* Get a name to represent the issuer of this certificate.
*
* This will try to lookup the common name, orianizational unit,
* organization in that order.
*
* Returns: (nullable): the allocated issuer name, or %NULL if no issuer name
*/
gchar *
gcr_certificate_get_issuer_name (GcrCertificate *self)
{
gchar *name;
name = gcr_certificate_get_issuer_part (self, "cn");
if (name == NULL)
name = gcr_certificate_get_issuer_part (self, "ou");
if (name == NULL)
name = gcr_certificate_get_issuer_part (self, "o");
return name;
}
/**
* gcr_certificate_get_issuer_cn:
* @self: a #GcrCertificate
*
* Get the common name of the issuer of this certificate.
*
* The string returned should be freed by the caller when no longer
* required.
*
* Returns: (nullable): The allocated issuer CN, or %NULL if no issuer CN present.
*/
gchar*
gcr_certificate_get_issuer_cn (GcrCertificate *self)
{
return gcr_certificate_get_issuer_part (self, "cn");
}
/**
* gcr_certificate_get_issuer_part:
* @self: a #GcrCertificate
* @part: a DN type string or OID.
*
* Get a part of the DN of the issuer of this certificate.
*
* Examples of a @part might be the 'OU' (organizational unit)
* or the 'CN' (common name). Only the value of that part
* of the DN is returned.
*
* The string returned should be freed by the caller when no longer
* required.
*
* Returns: (nullable): the allocated part of the issuer DN, or %NULL if no
* such part is present
*/
gchar *
gcr_certificate_get_issuer_part (GcrCertificate *self, const char *part)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (part != NULL, NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_dn_read_part (egg_asn1x_node (info->asn1, "tbsCertificate", "issuer", "rdnSequence", NULL), part);
}
static GBytes *
_gcr_certificate_get_issuer_const (GcrCertificate *self)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_asn1x_get_element_raw (egg_asn1x_node (info->asn1, "tbsCertificate", "issuer", NULL));
}
/**
* gcr_certificate_get_issuer_raw:
* @self: a #GcrCertificate
* @n_data: (out): The length of the returned data.
*
* Get the raw DER data for the issuer DN of the certificate.
*
* The data should be freed by using g_free() when no longer required.
*
* Returns: (transfer full) (array length=n_data) (nullable): allocated memory
* containing the raw issuer
*/
guchar *
gcr_certificate_get_issuer_raw (GcrCertificate *self,
gsize *n_data)
{
GBytes *bytes;
guchar *result;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (n_data != NULL, NULL);
bytes = _gcr_certificate_get_issuer_const (self);
if (bytes == NULL) {
*n_data = 0;
return NULL;
}
*n_data = g_bytes_get_size (bytes);
result = g_memdup2 (g_bytes_get_data (bytes, NULL), *n_data);
g_bytes_unref (bytes);
return result;
}
/**
* gcr_certificate_is_issuer:
* @self: a #GcrCertificate
* @issuer: a possible issuer #GcrCertificate
*
* Check if @issuer could be the issuer of this certificate. This is done by
* comparing the relevant subject and issuer fields. No signature check is
* done. Proper verification of certificates must be done via a crypto
* library.
*
* Returns: whether @issuer could be the issuer of the certificate.
*/
gboolean
gcr_certificate_is_issuer (GcrCertificate *self, GcrCertificate *issuer)
{
GBytes *subject_dn;
GBytes *issuer_dn;
gboolean ret;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), FALSE);
g_return_val_if_fail (GCR_IS_CERTIFICATE (issuer), FALSE);
subject_dn = _gcr_certificate_get_subject_const (issuer);
if (subject_dn == NULL)
return FALSE;
issuer_dn = _gcr_certificate_get_issuer_const (self);
if (issuer_dn == NULL)
return FALSE;
ret = g_bytes_equal (subject_dn, issuer_dn);
g_bytes_unref (subject_dn);
g_bytes_unref (issuer_dn);
return ret;
}
/**
* gcr_certificate_get_issuer_dn:
* @self: a #GcrCertificate
*
* Get the full issuer DN of the certificate as a (mostly)
* readable string.
*
* The string returned should be freed by the caller when no longer
* required.
*
* Returns: (nullable): The allocated issuer DN of the certificate.
*/
gchar*
gcr_certificate_get_issuer_dn (GcrCertificate *self)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_dn_read (egg_asn1x_node (info->asn1, "tbsCertificate", "issuer", "rdnSequence", NULL));
}
/**
* gcr_certificate_get_subject_cn:
* @self: a #GcrCertificate
*
* Get the common name of the subject of this certificate.
*
* The string returned should be freed by the caller when no longer
* required.
*
* Returns: (nullable): The allocated subject CN, or %NULL if no subject CN present.
*/
gchar*
gcr_certificate_get_subject_cn (GcrCertificate *self)
{
return gcr_certificate_get_subject_part (self, "cn");
}
/**
* gcr_certificate_get_subject_name:
* @self: a #GcrCertificate
*
* Get a name to represent the subject of this certificate.
*
* This will try to lookup the common name, orianizational unit,
* organization in that order.
*
* Returns: (nullable): the allocated subject name, or %NULL if no subject name
*/
gchar *
gcr_certificate_get_subject_name (GcrCertificate *self)
{
gchar *name;
name = gcr_certificate_get_subject_part (self, "cn");
if (name == NULL)
name = gcr_certificate_get_subject_part (self, "ou");
if (name == NULL)
name = gcr_certificate_get_subject_part (self, "o");
return name;
}
/**
* gcr_certificate_get_subject_part:
* @self: a #GcrCertificate
* @part: a DN type string or OID.
*
* Get a part of the DN of the subject of this certificate.
*
* Examples of a @part might be the 'OU' (organizational unit)
* or the 'CN' (common name). Only the value of that part
* of the DN is returned.
*
* The string returned should be freed by the caller when no longer
* required.
*
* Returns: (nullable): the allocated part of the subject DN, or %NULL if no
* such part is present.
*/
gchar*
gcr_certificate_get_subject_part (GcrCertificate *self, const char *part)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (part != NULL, NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_dn_read_part (egg_asn1x_node (info->asn1, "tbsCertificate", "subject", "rdnSequence", NULL), part);
}
/**
* gcr_certificate_get_subject_dn:
* @self: a #GcrCertificate
*
* Get the full subject DN of the certificate as a (mostly)
* readable string.
*
* The string returned should be freed by the caller when no longer
* required.
*
* Returns: (nullable): The allocated subject DN of the certificate.
*/
gchar*
gcr_certificate_get_subject_dn (GcrCertificate *self)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_dn_read (egg_asn1x_node (info->asn1, "tbsCertificate", "subject", "rdnSequence", NULL));
}
static GBytes *
_gcr_certificate_get_subject_const (GcrCertificate *self)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_asn1x_get_element_raw (egg_asn1x_node (info->asn1, "tbsCertificate", "subject", NULL));
}
/**
* gcr_certificate_get_subject_raw:
* @self: a #GcrCertificate
* @n_data: (out): The length of the returned data.
*
* Get the raw DER data for the subject DN of the certificate.
*
* The data should be freed by using g_free() when no longer required.
*
* Returns: (transfer full) (array length=n_data) (nullable): allocated memory
* containing the raw subject
*/
guchar *
gcr_certificate_get_subject_raw (GcrCertificate *self, gsize *n_data)
{
GBytes *bytes;
guchar *result;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (n_data != NULL, NULL);
bytes = _gcr_certificate_get_subject_const (self);
if (bytes == NULL) {
*n_data = 0;
return NULL;
}
*n_data = g_bytes_get_size (bytes);
result = g_memdup2 (g_bytes_get_data (bytes, NULL), *n_data);
g_bytes_unref (bytes);
return result;
}
/**
* gcr_certificate_get_issued_date:
* @self: a #GcrCertificate
*
* Get the issued date of this certificate.
*
* Returns: (nullable): A issued date of this certificate.
*/
GDateTime *
gcr_certificate_get_issued_date (GcrCertificate *self)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_asn1x_get_time_as_date_time (egg_asn1x_node (info->asn1, "tbsCertificate", "validity", "notBefore", NULL));
}
/**
* gcr_certificate_get_expiry_date:
* @self: a #GcrCertificate
*
* Get the expiry date of this certificate.
*
* Returns: (nullable): An expiry date of this certificate.
*/
GDateTime *
gcr_certificate_get_expiry_date (GcrCertificate *self)
{
GcrCertificateInfo *info;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
info = certificate_info_load (self);
if (info == NULL)
return NULL;
return egg_asn1x_get_time_as_date_time (egg_asn1x_node (info->asn1, "tbsCertificate", "validity", "notAfter", NULL));
}
/**
* gcr_certificate_get_key_size:
* @self: a #GcrCertificate
*
* Get the key size in bits of the public key represented
* by this certificate.
*
* Returns: The key size of the certificate.
*/
guint
gcr_certificate_get_key_size (GcrCertificate *self)
{
GcrCertificateInfo *info;
GNode *subject_public_key;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), 0);
info = certificate_info_load (self);
if (info == NULL)
return 0;
if (!info->key_size) {
subject_public_key = egg_asn1x_node (info->asn1, "tbsCertificate",
"subjectPublicKeyInfo", NULL);
info->key_size = _gcr_subject_public_key_calculate_size (subject_public_key);
}
return info->key_size;
}
/**
* gcr_certificate_get_fingerprint:
* @self: a #GcrCertificate
* @type: the type of algorithm for the fingerprint.
* @n_length: (out): The length of the resulting fingerprint.
*
* Calculate the fingerprint for this certificate.
*
* The caller should free the returned data using g_free() when
* it is no longer required.
*
* Returns: (array length=n_length) (nullable): the raw binary fingerprint
**/
guchar *
gcr_certificate_get_fingerprint (GcrCertificate *self, GChecksumType type, gsize *n_length)
{
GChecksum *sum;
guchar *digest;
gssize length;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (n_length != NULL, NULL);
sum = digest_certificate (self, type);
if (sum == NULL) {
*n_length = 0;
return NULL;
}
length = g_checksum_type_get_length (type);
g_return_val_if_fail (length > 0, NULL);
digest = g_malloc (length);
*n_length = length;
g_checksum_get_digest (sum, digest, n_length);
g_checksum_free (sum);
return digest;
}
/**
* gcr_certificate_get_fingerprint_hex:
* @self: a #GcrCertificate
* @type: the type of algorithm for the fingerprint.
*
* Calculate the fingerprint for this certificate, and return it
* as a hex string.
*
* The caller should free the returned data using g_free() when
* it is no longer required.
*
* Returns: (nullable): an allocated hex string which contains the fingerprint.
*/
gchar*
gcr_certificate_get_fingerprint_hex (GcrCertificate *self, GChecksumType type)
{
GChecksum *sum;
guchar *digest;
gsize n_digest;
gssize length;
gchar *hex;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
sum = digest_certificate (self, type);
if (sum == NULL)
return NULL;
length = g_checksum_type_get_length (type);
g_return_val_if_fail (length > 0, NULL);
digest = g_malloc (length);
n_digest = length;
g_checksum_get_digest (sum, digest, &n_digest);
hex = egg_hex_encode_full (digest, n_digest, TRUE, " ", 1);
g_checksum_free (sum);
g_free (digest);
return hex;
}
/**
* gcr_certificate_get_serial_number:
* @self: a #GcrCertificate
* @n_length: (out): the length of the returned data.
*
* Get the raw binary serial number of the certificate.
*
* The caller should free the returned data using g_free() when
* it is no longer required.
*
* Returns: (array length=n_length) (nullable): the raw binary serial number.
*/
guchar *
gcr_certificate_get_serial_number (GcrCertificate *self, gsize *n_length)
{
GcrCertificateInfo *info;
GBytes *bytes;
guchar *result;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (n_length != NULL, NULL);
info = certificate_info_load (self);
if (info == NULL) {
*n_length = 0;
return NULL;
}
bytes = egg_asn1x_get_integer_as_raw (egg_asn1x_node (info->asn1, "tbsCertificate", "serialNumber", NULL));
g_return_val_if_fail (bytes != NULL, NULL);
*n_length = g_bytes_get_size (bytes);
result = g_memdup2 (g_bytes_get_data (bytes, NULL), *n_length);
g_bytes_unref (bytes);
return result;
}
/**
* gcr_certificate_get_serial_number_hex:
* @self: a #GcrCertificate
*
* Get the serial number of the certificate as a hex string.
*
* The caller should free the returned data using g_free() when
* it is no longer required.
*
* Returns: (nullable): an allocated string containing the serial number as hex.
*/
gchar*
gcr_certificate_get_serial_number_hex (GcrCertificate *self)
{
guchar *serial;
gsize n_serial;
gchar *hex;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
serial = gcr_certificate_get_serial_number (self, &n_serial);
if (serial == NULL)
return NULL;
hex = egg_hex_encode (serial, n_serial);
g_free (serial);
return hex;
}
/**
* gcr_certificate_get_basic_constraints:
* @self: the certificate
* @is_ca: (out) (optional): location to place a %TRUE if is an authority
* @path_len: (out) (optional): location to place the max path length
*
* Get the basic constraints for the certificate if present. If %FALSE is
* returned then no basic constraints are present and the @is_ca and
* @path_len arguments are not changed.
*
* Returns: whether basic constraints are present or not
*/
gboolean
gcr_certificate_get_basic_constraints (GcrCertificate *self,
gboolean *is_ca,
gint *path_len)
{
GcrCertificateInfo *info;
GBytes *value;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), FALSE);
info = certificate_info_load (self);
if (info == NULL)
return FALSE;
value = _gcr_certificate_extension_find (info->asn1, GCR_OID_BASIC_CONSTRAINTS, NULL);
if (!value)
return FALSE;
if (!_gcr_certificate_extension_basic_constraints (value, is_ca, path_len))
g_return_val_if_reached (FALSE);
g_bytes_unref (value);
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"), 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);
if (display == NULL)
display = g_strdup (_("Unknown"));
_gcr_certificate_section_new_field_take_value (section, _("Identity"), g_steal_pointer (&display));
display = gcr_certificate_get_issuer_cn (self);
if (display == NULL)
display = g_strdup (_("Unknown"));
_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, "%F");
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 (§ion));
/* 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 (§ion));
/* 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 (§ion));
/* 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, "%F");
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, "%F");
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 (§ion));
/* 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 (§ion));
/* 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 (§ion));
/* 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 (§ion));
}
/* 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 (§ion));
return g_list_reverse (list);
}
/* -----------------------------------------------------------------------------
* MIXIN
*/
/**
* gcr_certificate_mixin_emit_notify:
* @self: the #GcrCertificate
*
* Implementers of the #GcrCertificate mixin should call this function to notify
* when the certificate has changed to emit notifications on the various
* properties.
*/
void
gcr_certificate_mixin_emit_notify (GcrCertificate *self)
{
GObject *obj;
g_return_if_fail (GCR_IS_CERTIFICATE (self));
obj = G_OBJECT (self);
g_object_notify (obj, "label");
g_object_notify (obj, "subject-name");
g_object_notify (obj, "issuer-name");
g_object_notify (obj, "expiry-date");
}
/**
* gcr_certificate_mixin_class_init: (skip)
* @object_class: The GObjectClass for this class
*
* Initialize the certificate mixin for the class. This mixin implements the
* various required properties for the certificate.
*
* Call this function near the end of your derived class_init function. The
* derived class must implement the #GcrCertificate interface.
*/
void
gcr_certificate_mixin_class_init (GObjectClass *object_class)
{
if (!g_object_class_find_property (object_class, "description"))
g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
if (!g_object_class_find_property (object_class, "label"))
g_object_class_override_property (object_class, PROP_LABEL, "label");
if (!g_object_class_find_property (object_class, "subject-name"))
g_object_class_override_property (object_class, PROP_SUBJECT_NAME, "subject-name");
if (!g_object_class_find_property (object_class, "issuer-name"))
g_object_class_override_property (object_class, PROP_ISSUER_NAME, "issuer-name");
if (!g_object_class_find_property (object_class, "expiry-date"))
g_object_class_override_property (object_class, PROP_EXPIRY_DATE, "expiry-date");
_gcr_initialize_library ();
}
/**
* gcr_certificate_mixin_get_property: (skip)
* @obj: The object
* @prop_id: The property id
* @value: The value to fill in.
* @pspec: The param specification.
*
* Implementation to get various required certificate properties. This should
* be called from your derived class get_property function, or used as a
* get_property virtual function.
*
* Example of use as called from derived class get_property function:
*
*
* static void
* my_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec)
* {
* switch (prop_id) {
*
* ...
*
* default:
* gcr_certificate_mixin_get_property (obj, prop_id, value, pspec);
* break;
* }
*}
*
*
* Example of use as get_property function:
*
*
* static void
* my_class_init (MyClass *klass)
* {
* GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
* gobject_class->get_property = gcr_certificate_mixin_get_property;
*
* ...
* }
*
*/
void
gcr_certificate_mixin_get_property (GObject *obj, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GcrCertificate *cert = GCR_CERTIFICATE (obj);
switch (prop_id) {
case PROP_LABEL:
g_value_take_string (value, gcr_certificate_get_subject_name (cert));
break;
case PROP_SUBJECT_NAME:
g_value_take_string (value, gcr_certificate_get_subject_name (cert));
break;
case PROP_DESCRIPTION:
g_value_set_string (value, _("Certificate"));
break;
case PROP_ISSUER_NAME:
g_value_take_string (value, gcr_certificate_get_issuer_name (cert));
break;
case PROP_EXPIRY_DATE:
g_value_take_boxed (value, gcr_certificate_get_expiry_date (cert));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}