aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--smime/ChangeLog45
-rw-r--r--smime/gui/certificate-viewer.c72
-rw-r--r--smime/lib/e-asn1-object.c62
-rw-r--r--smime/lib/e-asn1-object.h52
-rw-r--r--smime/lib/e-cert.c741
-rw-r--r--smime/lib/e-cert.h4
6 files changed, 937 insertions, 39 deletions
diff --git a/smime/ChangeLog b/smime/ChangeLog
index 7794f799f6..c5b644d9a2 100644
--- a/smime/ChangeLog
+++ b/smime/ChangeLog
@@ -1,3 +1,48 @@
+2003-12-04 Chris Toshok <toshok@ximian.com>
+
+ * lib/e-cert.h: add prototype for e_cert_get_asn1_struct.
+
+ * lib/e-cert.c (e_cert_dispose): unref the asn1 object.
+ (get_int_value): copy and massage from mozilla source.
+ (process_version): same.
+ (process_serial_number_der): same.
+ (get_default_oid_format): same.
+ (get_oid_text): same.
+ (process_raw_bytes): same.
+ (process_sec_algorithm_id): same.
+ (process_subject_public_key_info): same.
+ (process_ns_cert_type_extensions): same.
+ (process_key_usage_extensions): same.
+ (process_extension_data): same.
+ (process_single_extension): same.
+ (process_extensions): same.
+ (process_name): same.
+ (create_tbs_certificate_asn1_struct): same.
+ (create_asn1_struct): same.
+ (e_cert_get_asn1_struct): new function.
+
+ * lib/e-asn1-object.c (e_asn1_object_dispose): free the display
+ name, value, and children.
+ (e_asn1_object_init): assume it's a valid container unless we hear
+ otherwise.
+ (e_asn1_object_new_from_cert): nuke.
+ (e_asn1_object_set_valid_container): implement.
+ (e_asn1_object_append_child): same.
+ (e_asn1_object_set_display_name): same.
+ (e_asn1_object_set_display_value): same.
+
+ * lib/e-asn1-object.h: add prototypes for
+ e_asn1_object_set_valid_container, e_asn1_object_set_display_name,
+ e_asn1_object_set_display_value, and e_asn1_object_append_child.
+
+ * gui/certificate-viewer.c (populate_fields_tree): populate the
+ tree from the asn structure.
+ (hierarchy_selection_changed): blow away the old fields_tree
+ content and populate it again.
+ (fields_selection_changed): implement, set the text view's
+ contents to the asn1 object's display_value.
+ (fill_in_details): expand all nodes in the hierarchy tree.
+
2003-12-03 Chris Toshok <toshok@ximian.com>
* lib/Makefile.am (libessmime_la_SOURCES): add e-asn1-object.[ch]
diff --git a/smime/gui/certificate-viewer.c b/smime/gui/certificate-viewer.c
index 9314ac5c1e..e897597319 100644
--- a/smime/gui/certificate-viewer.c
+++ b/smime/gui/certificate-viewer.c
@@ -118,6 +118,32 @@ fill_in_general (CertificateViewerData *cvm_data, ECert *cert)
}
static void
+populate_fields_tree (CertificateViewerData *cvm_data, EASN1Object *asn1, GtkTreeIter *root)
+{
+ GtkTreeIter new_iter;
+
+ /* first insert a node for the current asn1 */
+ gtk_tree_store_insert (cvm_data->fields_store, &new_iter, root, -1);
+ gtk_tree_store_set (cvm_data->fields_store, &new_iter,
+ 0, e_asn1_object_get_display_name (asn1),
+ 1, asn1,
+ -1);
+
+ if (e_asn1_object_is_valid_container (asn1)) {
+ GList *children = e_asn1_object_get_children (asn1);
+ if (children) {
+ GList *l;
+ for (l = children; l; l = l->next) {
+ EASN1Object *subasn1 = l->data;
+ populate_fields_tree (cvm_data, subasn1, &new_iter);
+ }
+ }
+ g_list_foreach (children, (GFunc)g_object_unref, NULL);
+ g_list_free (children);
+ }
+}
+
+static void
hierarchy_selection_changed (GtkTreeSelection *selection, CertificateViewerData *cvm_data)
{
GtkTreeIter iter;
@@ -137,14 +163,51 @@ hierarchy_selection_changed (GtkTreeSelection *selection, CertificateViewerData
if (!cert)
return;
- /* XXX show the selected fields somehow */
- asn1_object = e_asn1_object_new_from_cert (cert);
+ /* display the cert's ASN1 structure */
+ asn1_object = e_cert_get_asn1_struct (cert);
+
+ /* wipe out the old model */
+ cvm_data->fields_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (cvm_data->fields_tree),
+ GTK_TREE_MODEL (cvm_data->fields_store));
+
+ /* populate the fields from the newly selected cert */
+ populate_fields_tree (cvm_data, asn1_object, NULL);
+ gtk_tree_view_expand_all (GTK_TREE_VIEW (cvm_data->fields_tree));
+ g_object_unref (asn1_object);
+
+ /* and blow away the field value */
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (cvm_data->field_text)),
+ "", 0);
}
}
static void
fields_selection_changed (GtkTreeSelection *selection, CertificateViewerData *cvm_data)
{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection,
+ &model,
+ &iter)) {
+ EASN1Object *asn1_object;
+ const char *value;
+
+ gtk_tree_model_get (model,
+ &iter,
+ 1, &asn1_object,
+ -1);
+
+ value = e_asn1_object_get_display_value (asn1_object);
+
+ if (value)
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (cvm_data->field_text)),
+ value, strlen (value));
+ else
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (cvm_data->field_text)),
+ "", 0);
+ }
}
static void
@@ -168,10 +231,7 @@ fill_in_details (CertificateViewerData *cvm_data, ECert *cert)
g_signal_connect (selection, "changed", G_CALLBACK (hierarchy_selection_changed), cvm_data);
/* hook up all the fields tree foo */
- cvm_data->fields_store = gtk_tree_store_new (1, G_TYPE_STRING);
cvm_data->fields_tree = glade_xml_get_widget (cvm_data->gui, "cert-fields-treeview");
- gtk_tree_view_set_model (GTK_TREE_VIEW (cvm_data->fields_tree),
- GTK_TREE_MODEL (cvm_data->fields_store));
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (cvm_data->fields_tree),
-1, "Field", gtk_cell_renderer_text_new(),
@@ -203,6 +263,8 @@ fill_in_details (CertificateViewerData *cvm_data, ECert *cert)
root = &new_iter;
}
+
+ gtk_tree_view_expand_all (GTK_TREE_VIEW (cvm_data->hierarchy_tree));
}
GtkWidget*
diff --git a/smime/lib/e-asn1-object.c b/smime/lib/e-asn1-object.c
index 47cf591b48..b7528dcd22 100644
--- a/smime/lib/e-asn1-object.c
+++ b/smime/lib/e-asn1-object.c
@@ -79,6 +79,21 @@ static GObjectClass *parent_class;
static void
e_asn1_object_dispose (GObject *object)
{
+ EASN1Object *obj = E_ASN1_OBJECT (object);
+ if (obj->priv) {
+
+ if (obj->priv->display_name)
+ g_free (obj->priv->display_name);
+
+ if (obj->priv->value)
+ g_free (obj->priv->value);
+
+ g_list_foreach (obj->priv->children, (GFunc)g_object_unref, NULL);
+ g_list_free (obj->priv->children);
+
+ g_free (obj->priv);
+ obj->priv = NULL;
+ }
}
static void
@@ -97,6 +112,8 @@ static void
e_asn1_object_init (EASN1Object *asn1)
{
asn1->priv = g_new0 (EASN1ObjectPrivate, 1);
+
+ asn1->priv->valid_container = TRUE;
}
GType
@@ -296,31 +313,18 @@ e_asn1_object_new_from_der (char *data, guint32 len)
}
EASN1Object*
-e_asn1_object_new_from_cert (ECert *cert)
-{
- char *data;
- guint32 len;
- EASN1Object *obj;
-
- if (!e_cert_get_raw_der (cert, &data, &len))
- return NULL;
-
- obj = g_object_new (E_TYPE_ASN1_OBJECT, NULL);
- if (!build_from_der (obj, data, data + len)) {
- g_object_unref (obj);
- return NULL;
- }
-
- return obj;
-}
-
-EASN1Object*
e_asn1_object_new (void)
{
return E_ASN1_OBJECT (g_object_new (E_TYPE_ASN1_OBJECT, NULL));
}
+void
+e_asn1_object_set_valid_container (EASN1Object *obj, gboolean flag)
+{
+ obj->priv->valid_container = flag;
+}
+
gboolean
e_asn1_object_is_valid_container (EASN1Object *obj)
{
@@ -349,12 +353,32 @@ e_asn1_object_get_children (EASN1Object *obj)
return children;
}
+void
+e_asn1_object_append_child (EASN1Object *parent, EASN1Object *child)
+{
+ parent->priv->children = g_list_append (parent->priv->children, g_object_ref (child));
+}
+
+void
+e_asn1_object_set_display_name (EASN1Object *obj, const char *name)
+{
+ g_free (obj->priv->display_name);
+ obj->priv->display_name = g_strdup (name);
+}
+
const char*
e_asn1_object_get_display_name (EASN1Object *obj)
{
return obj->priv->display_name;
}
+void
+e_asn1_object_set_display_value (EASN1Object *obj, const char *value)
+{
+ g_free (obj->priv->value);
+ obj->priv->value = g_strdup (value);
+}
+
const char*
e_asn1_object_get_display_value (EASN1Object *obj)
{
diff --git a/smime/lib/e-asn1-object.h b/smime/lib/e-asn1-object.h
index ebdb7de15e..76e2530fcc 100644
--- a/smime/lib/e-asn1-object.h
+++ b/smime/lib/e-asn1-object.h
@@ -25,8 +25,6 @@
#include <glib-object.h>
-#include "e-cert.h"
-
#include <nspr.h>
#define E_TYPE_ASN1_OBJECT (e_asn1_object_get_type ())
@@ -41,9 +39,32 @@ typedef struct _EASN1ObjectClass EASN1ObjectClass;
typedef struct _EASN1ObjectPrivate EASN1ObjectPrivate;
enum {
- E_ASN1_OBJECT_TYPE_APPLICATION,
- E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC,
- E_ASN1_OBJECT_TYPE_PRIVATE
+ /*
+ * Identifiers for the possible types of object.
+ */
+ E_ASN1_OBJECT_TYPE_END_CONTENTS = 0,
+ E_ASN1_OBJECT_TYPE_BOOLEAN = 1,
+ E_ASN1_OBJECT_TYPE_INTEGER = 2,
+ E_ASN1_OBJECT_TYPE_BIT_STRING = 3,
+ E_ASN1_OBJECT_TYPE_OCTET_STRING = 4,
+ E_ASN1_OBJECT_TYPE_NULL = 5,
+ E_ASN1_OBJECT_TYPE_OBJECT_ID = 6,
+ E_ASN1_OBJECT_TYPE_ENUMERATED = 10,
+ E_ASN1_OBJECT_TYPE_UTF8_STRING = 12,
+ E_ASN1_OBJECT_TYPE_SEQUENCE = 16,
+ E_ASN1_OBJECT_TYPE_SET = 17,
+ E_ASN1_OBJECT_TYPE_PRINTABLE_STRING = 19,
+ E_ASN1_OBJECT_TYPE_T61_STRING = 20,
+ E_ASN1_OBJECT_TYPE_IA5_STRING = 22,
+ E_ASN1_OBJECT_TYPE_UTC_TIME = 23,
+ E_ASN1_OBJECT_TYPE_GEN_TIME = 24,
+ E_ASN1_OBJECT_TYPE_VISIBLE_STRING = 26,
+ E_ASN1_OBJECT_TYPE_UNIVERSAL_STRING = 28,
+ E_ASN1_OBJECT_TYPE_BMP_STRING = 30,
+ E_ASN1_OBJECT_TYPE_HIGH_TAG_NUMBER = 31,
+ E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC = 32,
+ E_ASN1_OBJECT_TYPE_APPLICATION = 33,
+ E_ASN1_OBJECT_TYPE_PRIVATE = 34,
};
struct _EASN1Object {
@@ -63,18 +84,21 @@ struct _EASN1ObjectClass {
void (*_ecert_reserved4) (void);
};
-EASN1Object *e_asn1_object_new_from_cert (ECert *cert);
EASN1Object *e_asn1_object_new_from_der (char *data, guint32 len);
EASN1Object *e_asn1_object_new (void);
-gboolean e_asn1_object_is_valid_container (EASN1Object *obj);
-PRUint32 e_asn1_object_get_asn1_type (EASN1Object *obj);
-PRUint32 e_asn1_object_get_asn1_tag (EASN1Object *obj);
-GList *e_asn1_object_get_children (EASN1Object *obj);
-const char *e_asn1_object_get_display_name (EASN1Object *obj);
-const char *e_asn1_object_get_display_value (EASN1Object *obj);
-
-void e_asn1_object_get_data (EASN1Object *obj, char **data, guint32 *len);
+void e_asn1_object_set_valid_container (EASN1Object *obj, gboolean flag);
+gboolean e_asn1_object_is_valid_container (EASN1Object *obj);
+PRUint32 e_asn1_object_get_asn1_type (EASN1Object *obj);
+PRUint32 e_asn1_object_get_asn1_tag (EASN1Object *obj);
+GList *e_asn1_object_get_children (EASN1Object *obj);
+void e_asn1_object_append_child (EASN1Object *parent, EASN1Object *child);
+void e_asn1_object_set_display_name (EASN1Object *obj, const char *name);
+const char *e_asn1_object_get_display_name (EASN1Object *obj);
+void e_asn1_object_set_display_value (EASN1Object *obj, const char *value);
+const char *e_asn1_object_get_display_value (EASN1Object *obj);
+
+void e_asn1_object_get_data (EASN1Object *obj, char **data, guint32 *len);
GType e_asn1_object_get_type (void);
diff --git a/smime/lib/e-cert.c b/smime/lib/e-cert.c
index 2389cc29c8..173f445c22 100644
--- a/smime/lib/e-cert.c
+++ b/smime/lib/e-cert.c
@@ -90,6 +90,8 @@ struct _ECertPrivate {
char *sha1_fingerprint;
char *md5_fingerprint;
+ EASN1Object *asn1;
+
gboolean delete;
};
@@ -130,6 +132,9 @@ e_cert_dispose (GObject *object)
if (ec->priv->md5_fingerprint)
PORT_Free (ec->priv->md5_fingerprint);
+ if (ec->priv->asn1)
+ g_object_unref (ec->priv->asn1);
+
if (ec->priv->delete) {
printf ("attempting to delete cert marked for deletion\n");
if (e_cert_get_cert_type (ec) == E_CERT_USER) {
@@ -434,6 +439,742 @@ e_cert_get_chain (ECert *ecert)
return l;
}
+static gboolean
+get_int_value (SECItem *versionItem,
+ unsigned long *version)
+{
+ SECStatus srv;
+ srv = SEC_ASN1DecodeInteger(versionItem,version);
+ if (srv != SECSuccess) {
+ g_warning ("could not decode version of cert");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+process_version (SECItem *versionItem,
+ EASN1Object **retItem)
+{
+ EASN1Object *item = e_asn1_object_new ();
+ unsigned long version;
+
+ e_asn1_object_set_display_name (item, _("Version"));
+
+ /* Now to figure out what version this certificate is. */
+
+ if (versionItem->data) {
+ if (!get_int_value (versionItem, &version))
+ return FALSE;
+ } else {
+ /* If there is no version present in the cert, then rfc2459
+ says we default to v1 (0) */
+ version = 0;
+ }
+
+ switch (version){
+ case 0:
+ e_asn1_object_set_display_value (item, _("Version 1"));
+ break;
+ case 1:
+ e_asn1_object_set_display_value (item, _("Version 2"));
+ break;
+ case 2:
+ e_asn1_object_set_display_value (item, _("Version 3"));
+ break;
+ default:
+ g_warning ("Bad value for cert version");
+ return FALSE;
+ }
+
+ *retItem = item;
+ return TRUE;
+}
+
+static gboolean
+process_serial_number_der (SECItem *serialItem,
+ EASN1Object **retItem)
+{
+ char *serialNumber;
+ EASN1Object *item = e_asn1_object_new ();
+
+ e_asn1_object_set_display_name (item, _("Serial Number"));
+
+ serialNumber = CERT_Hexify(serialItem, 1);
+
+ e_asn1_object_set_display_value (item, serialNumber);
+ PORT_Free (serialNumber); /* XXX the right free to use? */
+
+ *retItem = item;
+ return TRUE;
+}
+
+static gboolean
+get_default_oid_format (SECItem *oid,
+ char **text)
+{
+ char buf[300];
+ unsigned int len;
+ int written;
+
+ unsigned long val = oid->data[0];
+ unsigned int i = val % 40;
+ val /= 40;
+ written = PR_snprintf(buf, 300, "%lu %u ", val, i);
+ if (written < 0)
+ return FALSE;
+ len = written;
+
+ val = 0;
+ for (i = 1; i < oid->len; ++i) {
+ /* In this loop, we have to parse a DER formatted
+ If the first bit is a 1, then the integer is
+ represented by more than one byte. If the
+ first bit is set then we continue on and add
+ the values of the later bytes until we get
+ a byte without the first bit set.
+ */
+ unsigned long j;
+
+ j = oid->data[i];
+ val = (val << 7) | (j & 0x7f);
+ if (j & 0x80)
+ continue;
+ written = PR_snprintf(&buf[len], sizeof(buf)-len, "%lu ", val);
+ if (written < 0)
+ return FALSE;
+
+ len += written;
+ if (len >= sizeof (buf))
+ g_warning ("OID data to big to display in 300 chars.");
+ val = 0;
+ }
+
+ *text = g_strdup (buf);
+ return TRUE;
+}
+
+static gboolean
+get_oid_text (SECItem *oid, char **text)
+{
+ SECOidTag oidTag = SECOID_FindOIDTag(oid);
+ char *temp;
+
+ switch (oidTag) {
+ case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+ *text = g_strdup (_("PKCS #1 MD2 With RSA Encryption"));
+ break;
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ *text = g_strdup (_("PKCS #1 MD5 With RSA Encryption"));
+ break;
+ case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+ *text = g_strdup (_("PKCS #1 SHA-1 With RSA Encryption"));
+ break;
+ case SEC_OID_AVA_COUNTRY_NAME:
+ *text = g_strdup (_("C"));
+ break;
+ case SEC_OID_AVA_COMMON_NAME:
+ *text = g_strdup (_("CN"));
+ break;
+ case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
+ *text = g_strdup (_("OU"));
+ break;
+ case SEC_OID_AVA_ORGANIZATION_NAME:
+ *text = g_strdup (_("O"));
+ break;
+ case SEC_OID_AVA_LOCALITY:
+ *text = g_strdup (_("L"));
+ break;
+ case SEC_OID_AVA_DN_QUALIFIER:
+ *text = g_strdup (_("DN"));
+ break;
+ case SEC_OID_AVA_DC:
+ *text = g_strdup (_("DC"));
+ break;
+ case SEC_OID_AVA_STATE_OR_PROVINCE:
+ *text = g_strdup (_("ST"));
+ break;
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ *text = g_strdup (_("PKCS #1 RSA Encryption"));
+ break;
+ case SEC_OID_X509_KEY_USAGE:
+ *text = g_strdup (_("Certificate Key Usage"));
+ break;
+ case SEC_OID_NS_CERT_EXT_CERT_TYPE:
+ *text = g_strdup (_("Netscape Certificate Type"));
+ break;
+ case SEC_OID_X509_AUTH_KEY_ID:
+ *text = g_strdup (_("Certificate Authority Key Identifier"));
+ break;
+ case SEC_OID_RFC1274_UID:
+ *text = g_strdup (_("UID"));
+ break;
+ case SEC_OID_PKCS9_EMAIL_ADDRESS:
+ *text = g_strdup (_("E"));
+ break;
+ default:
+ if (!get_default_oid_format (oid, &temp))
+ return FALSE;
+
+ *text = g_strdup_printf (_("Object Identifier (%s)"), temp);
+ g_free (temp);
+
+ break;
+ }
+ return TRUE;
+}
+
+
+static gboolean
+process_raw_bytes (SECItem *data, char **text)
+{
+ /* This function is used to display some DER bytes
+ that we have not added support for decoding.
+ It prints the value of the byte out into a
+ string that can later be displayed as a byte
+ string. We place a new line after 24 bytes
+ to break up extermaly long sequence of bytes.
+ */
+ GString *str = g_string_new ("");
+ PRUint32 i;
+ char buffer[5];
+ for (i=0; i<data->len; i++) {
+ PR_snprintf(buffer, 5, "%02x ", data->data[i]);
+ g_string_append (str, buffer);
+ if ((i+1)%16 == 0) {
+ g_string_append (str, "\n");
+ }
+ }
+ *text = g_string_free (str, FALSE);
+ return TRUE;
+}
+
+static gboolean
+process_sec_algorithm_id (SECAlgorithmID *algID,
+ EASN1Object **retSequence)
+{
+ EASN1Object *sequence = e_asn1_object_new ();
+ char *text;
+
+ *retSequence = NULL;
+
+ get_oid_text (&algID->algorithm, &text);
+
+ if (!algID->parameters.len || algID->parameters.data[0] == E_ASN1_OBJECT_TYPE_NULL) {
+ e_asn1_object_set_display_value (sequence, text);
+ e_asn1_object_set_valid_container (sequence, FALSE);
+ } else {
+ EASN1Object *subitem;
+
+ subitem = e_asn1_object_new ();
+ e_asn1_object_set_display_name (subitem, _("Algorithm Identifier"));
+ e_asn1_object_set_display_value (subitem, text);
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+
+ g_free (text);
+
+ subitem = e_asn1_object_new ();
+ e_asn1_object_set_display_name (subitem, _("Algorithm Parameters"));
+ process_raw_bytes (&algID->parameters, &text);
+ e_asn1_object_set_display_value (subitem, text);
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+ }
+
+ g_free (text);
+ *retSequence = sequence;
+ return TRUE;
+}
+
+static gboolean
+process_subject_public_key_info (CERTSubjectPublicKeyInfo *spki,
+ EASN1Object *parentSequence)
+{
+ EASN1Object *spkiSequence = e_asn1_object_new();
+ EASN1Object *sequenceItem;
+ EASN1Object *printableItem;
+ char *text;
+
+ e_asn1_object_set_display_name (spkiSequence, _("Subject Public Key Info"));
+
+ if (!process_sec_algorithm_id (&spki->algorithm, &sequenceItem))
+ return FALSE;
+
+ e_asn1_object_set_display_name (sequenceItem, _("Subject Public Key Algorithm"));
+
+ e_asn1_object_append_child (spkiSequence, sequenceItem);
+
+ /* The subjectPublicKey field is encoded as a bit string.
+ ProcessRawBytes expects the lenght to be in bytes, so
+ let's convert the lenght into a temporary SECItem.
+ */
+ SECItem data;
+ data.data = spki->subjectPublicKey.data;
+ data.len = spki->subjectPublicKey.len / 8;
+
+ process_raw_bytes (&data, &text);
+ printableItem = e_asn1_object_new ();
+
+ e_asn1_object_set_display_value (printableItem, text);
+ e_asn1_object_set_display_name (printableItem, _("Subject's Public Key"));
+ e_asn1_object_append_child (spkiSequence, printableItem);
+ g_object_unref (printableItem);
+
+ e_asn1_object_append_child (parentSequence, spkiSequence);
+ g_object_unref (spkiSequence);
+
+ return TRUE;
+}
+
+static gboolean
+process_ns_cert_type_extensions (SECItem *extData,
+ GString *text)
+{
+ SECItem decoded;
+ unsigned char nsCertType;
+
+ decoded.data = NULL;
+ decoded.len = 0;
+ if (SECSuccess != SEC_ASN1DecodeItem(NULL, &decoded,
+ SEC_ASN1_GET(SEC_BitStringTemplate), extData)) {
+ g_string_append (text, _("Error: Unable to process extension"));
+ return TRUE;
+ }
+
+ nsCertType = decoded.data[0];
+
+ PORT_Free (decoded.data); /* XXX right free? */
+
+ if (nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
+ g_string_append (text, _("SSL Client Certificate"));
+ g_string_append (text, "\n");
+ }
+ if (nsCertType & NS_CERT_TYPE_SSL_SERVER) {
+ g_string_append (text, _("SSL Server Certificate"));
+ g_string_append (text, "\n");
+ }
+ if (nsCertType & NS_CERT_TYPE_EMAIL) {
+ g_string_append (text, _("Email"));
+ g_string_append (text, "\n");
+ }
+ if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING) {
+ g_string_append (text, _("Object Signer"));
+ g_string_append (text, "\n");
+ }
+ if (nsCertType & NS_CERT_TYPE_SSL_CA) {
+ g_string_append (text, _("SSL Certificate Authority"));
+ g_string_append (text, "\n");
+ }
+ if (nsCertType & NS_CERT_TYPE_EMAIL_CA) {
+ g_string_append (text, _("Email Certificate Authority"));
+ g_string_append (text, "\n");
+ }
+ if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA) {
+ g_string_append (text, _("Object Signer"));
+ g_string_append (text, "\n");
+ }
+ return TRUE;
+}
+
+static gboolean
+process_key_usage_extensions (SECItem *extData, GString *text)
+{
+ SECItem decoded;
+ unsigned char keyUsage;
+
+ decoded.data = NULL;
+ decoded.len = 0;
+ if (SECSuccess != SEC_ASN1DecodeItem(NULL, &decoded,
+ SEC_ASN1_GET(SEC_BitStringTemplate), extData)) {
+ g_string_append (text, _("Error: Unable to process extension"));
+ return TRUE;
+ }
+
+ keyUsage = decoded.data[0];
+ PORT_Free (decoded.data); /* XXX right free? */
+
+ if (keyUsage & KU_DIGITAL_SIGNATURE) {
+ g_string_append (text, _("Signing"));
+ g_string_append (text, "\n");
+ }
+ if (keyUsage & KU_NON_REPUDIATION) {
+ g_string_append (text, _("Non-repudiation"));
+ g_string_append (text, "\n");
+ }
+ if (keyUsage & KU_KEY_ENCIPHERMENT) {
+ g_string_append (text, _("Key Encipherment"));
+ g_string_append (text, "\n");
+ }
+ if (keyUsage & KU_DATA_ENCIPHERMENT) {
+ g_string_append (text, _("Data Encipherment"));
+ g_string_append (text, "\n");
+ }
+ if (keyUsage & KU_KEY_AGREEMENT) {
+ g_string_append (text, _("Key Agreement"));
+ g_string_append (text, "\n");
+ }
+ if (keyUsage & KU_KEY_CERT_SIGN) {
+ g_string_append (text, _("Certificate Signer"));
+ g_string_append (text, "\n");
+ }
+ if (keyUsage & KU_CRL_SIGN) {
+ g_string_append (text, _("CRL Signer"));
+ g_string_append (text, "\n");
+ }
+
+ return TRUE;
+}
+
+static gboolean
+process_extension_data (SECOidTag oidTag, SECItem *extData,
+ GString *str)
+{
+ gboolean rv;
+ switch (oidTag) {
+ case SEC_OID_NS_CERT_EXT_CERT_TYPE:
+ rv = process_ns_cert_type_extensions (extData, str);
+ break;
+ case SEC_OID_X509_KEY_USAGE:
+ rv = process_key_usage_extensions (extData, str);
+ break;
+ default: {
+ char *text;
+ rv = process_raw_bytes (extData, &text);
+ g_string_append (str, text);
+ g_free (text);
+ break;
+ }
+ }
+ return rv;
+}
+
+static gboolean
+process_single_extension (CERTCertExtension *extension,
+ EASN1Object **retExtension)
+{
+ GString *str = g_string_new ("");
+ char *text;
+ EASN1Object *extensionItem;
+ SECOidTag oidTag = SECOID_FindOIDTag(&extension->id);
+
+ get_oid_text (&extension->id, &text);
+
+ extensionItem = e_asn1_object_new ();
+
+ e_asn1_object_set_display_name (extensionItem, text);
+ g_free (text);
+
+ if (extension->critical.data != NULL) {
+ if (extension->critical.data[0]) {
+ g_string_append (str, _("Critical"));
+ } else {
+ g_string_append (str, _("Not Critical"));
+ }
+ } else {
+ g_string_append (str, _("Not Critical"));
+ }
+ g_string_append (str, "\n");
+ if (!process_extension_data (oidTag, &extension->value, str)) {
+ g_string_free (str, TRUE);
+ return FALSE;
+ }
+
+ e_asn1_object_set_display_value (extensionItem, str->str);
+ g_string_free (str, TRUE);
+ *retExtension = extensionItem;
+ return TRUE;
+}
+
+static gboolean
+process_extensions (CERTCertExtension **extensions,
+ EASN1Object *parentSequence)
+{
+ EASN1Object *extensionSequence = e_asn1_object_new ();
+ PRInt32 i;
+
+ e_asn1_object_set_display_name (extensionSequence, _("Extensions"));
+
+ for (i=0; extensions[i] != NULL; i++) {
+ EASN1Object *newExtension;
+
+ if (!process_single_extension (extensions[i],
+ &newExtension))
+ return FALSE;
+
+ e_asn1_object_append_child (extensionSequence, newExtension);
+ }
+ e_asn1_object_append_child (parentSequence, extensionSequence);
+ return TRUE;
+}
+
+static gboolean
+process_name (CERTName *name, char **value)
+{
+ CERTRDN** rdns;
+ CERTRDN** rdn;
+ CERTAVA** avas;
+ CERTAVA* ava;
+ SECItem *decodeItem = NULL;
+ GString *final_string = g_string_new ("");
+
+ char *type;
+ GString *avavalue;
+ char *temp;
+ CERTRDN **lastRdn;
+
+ rdns = name->rdns;
+
+ lastRdn = rdns;
+
+ /* find last RDN */
+ lastRdn = rdns;
+ while (*lastRdn) lastRdn++;
+
+ /* The above whille loop will put us at the last member
+ * of the array which is a NULL pointer. So let's back
+ * up one spot so that we have the last non-NULL entry in
+ * the array in preparation for traversing the
+ * RDN's (Relative Distinguished Name) in reverse order.
+ */
+ lastRdn--;
+
+ /*
+ * Loop over name contents in _reverse_ RDN order appending to string
+ * When building the Ascii string, NSS loops over these entries in
+ * reverse order, so I will as well. The difference is that NSS
+ * will always place them in a one line string separated by commas,
+ * where I want each entry on a single line. I can't just use a comma
+ * as my delimitter because it is a valid character to have in the
+ * value portion of the AVA and could cause trouble when parsing.
+ */
+ for (rdn = lastRdn; rdn >= rdns; rdn--) {
+ avas = (*rdn)->avas;
+ while ((ava = *avas++) != 0) {
+ if (!get_oid_text (&ava->type, &type))
+ return FALSE;
+
+ /* This function returns a string in UTF8 format. */
+ decodeItem = CERT_DecodeAVAValue(&ava->value);
+ if(!decodeItem) {
+ return FALSE;
+ }
+
+ avavalue = g_string_new_len ((char*)decodeItem->data, decodeItem->len);
+
+ SECITEM_FreeItem(decodeItem, PR_TRUE);
+
+ temp = g_strdup_printf (_("%s = %s"), type, avavalue->str);
+
+ g_string_append (final_string, temp);
+ g_string_append (final_string, "\n");
+ g_string_free (avavalue, TRUE);
+ g_free (temp);
+ }
+ }
+ *value = g_string_free (final_string, FALSE);
+ return TRUE;
+}
+
+static gboolean
+create_tbs_certificate_asn1_struct (ECert *cert, EASN1Object **seq)
+{
+ /*
+ ** TBSCertificate ::= SEQUENCE {
+ ** version [0] EXPLICIT Version DEFAULT v1,
+ ** serialNumber CertificateSerialNumber,
+ ** signature AlgorithmIdentifier,
+ ** issuer Name,
+ ** validity Validity,
+ ** subject Name,
+ ** subjectPublicKeyInfo SubjectPublicKeyInfo,
+ ** issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ ** -- If present, version shall be v2 or v3
+ ** subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ ** -- If present, version shall be v2 or v3
+ ** extensions [3] EXPLICIT Extensions OPTIONAL
+ ** -- If present, version shall be v3
+ ** }
+ **
+ ** This is the ASN1 structure we should be dealing with at this point.
+ ** The code in this method will assert this is the structure we're dealing
+ ** and then add more user friendly text for that field.
+ */
+ EASN1Object *sequence = e_asn1_object_new ();
+ char *text;
+ EASN1Object *subitem;
+ SECItem data;
+
+ e_asn1_object_set_display_name (sequence, _("Certificate"));
+
+ if (!process_version (&cert->priv->cert->version, &subitem))
+ return FALSE;
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+
+ if (!process_serial_number_der (&cert->priv->cert->serialNumber, &subitem))
+ return FALSE;
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+
+
+ if (!process_sec_algorithm_id (&cert->priv->cert->signature, &subitem))
+ return FALSE;
+ e_asn1_object_set_display_name (subitem, _("Certificate Signature Algorithm"));
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+
+ process_name (&cert->priv->cert->issuer, &text);
+ subitem = e_asn1_object_new ();
+ e_asn1_object_set_display_value (subitem, text);
+ g_free (text);
+
+ e_asn1_object_set_display_name (subitem, _("Issuer"));
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+
+#if notyet
+ nsCOMPtr<nsIASN1Sequence> validitySequence = new nsNSSASN1Sequence();
+ nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpValidity").get(),
+ text);
+ validitySequence->SetDisplayName(text);
+ asn1Objects->AppendElement(validitySequence, PR_FALSE);
+ nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpNotBefore").get(),
+ text);
+ nsCOMPtr<nsIX509CertValidity> validityData;
+ GetValidity(getter_AddRefs(validityData));
+ PRTime notBefore, notAfter;
+
+ validityData->GetNotBefore(&notBefore);
+ validityData->GetNotAfter(&notAfter);
+ validityData = 0;
+ rv = ProcessTime(notBefore, text.get(), validitySequence);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpNotAfter").get(),
+ text);
+ rv = ProcessTime(notAfter, text.get(), validitySequence);
+ if (NS_FAILED(rv))
+ return rv;
+#endif
+
+ subitem = e_asn1_object_new ();
+ e_asn1_object_set_display_name (subitem, _("Subject"));
+
+ process_name (&cert->priv->cert->subject, &text);
+ e_asn1_object_set_display_value (subitem, text);
+ g_free (text);
+ e_asn1_object_append_child (sequence, subitem);
+ g_object_unref (subitem);
+
+ if (!process_subject_public_key_info (&cert->priv->cert->subjectPublicKeyInfo, sequence))
+ return FALSE;
+
+ /* Is there an issuerUniqueID? */
+ if (cert->priv->cert->issuerID.data) {
+ /* The issuerID is encoded as a bit string.
+ The function ProcessRawBytes expects the
+ length to be in bytes, so let's convert the
+ length in a temporary SECItem
+ */
+ data.data = cert->priv->cert->issuerID.data;
+ data.len = cert->priv->cert->issuerID.len / 8;
+
+ subitem = e_asn1_object_new ();
+
+ e_asn1_object_set_display_name (subitem, _("Issuer Unique ID"));
+ process_raw_bytes (&data, &text);
+ e_asn1_object_set_display_value (subitem, text);
+ g_free (text);
+
+ e_asn1_object_append_child (sequence, subitem);
+ }
+
+ if (cert->priv->cert->subjectID.data) {
+ /* The subjectID is encoded as a bit string.
+ The function ProcessRawBytes expects the
+ length to be in bytes, so let's convert the
+ length in a temporary SECItem
+ */
+ data.data = cert->priv->cert->issuerID.data;
+ data.len = cert->priv->cert->issuerID.len / 8;
+
+ subitem = e_asn1_object_new ();
+
+ e_asn1_object_set_display_name (subitem, _("Subject Unique ID"));
+ process_raw_bytes (&data, &text);
+ e_asn1_object_set_display_value (subitem, text);
+ g_free (text);
+
+ e_asn1_object_append_child (sequence, subitem);
+ }
+ if (cert->priv->cert->extensions) {
+ if (!process_extensions (cert->priv->cert->extensions, sequence))
+ return FALSE;
+ }
+
+ *seq = sequence;
+
+ return TRUE;
+}
+
+static gboolean
+create_asn1_struct (ECert *cert)
+{
+ EASN1Object *sequence;
+ SECItem temp;
+ char *text;
+
+ cert->priv->asn1 = e_asn1_object_new ();
+
+ if (cert->priv->cert->nickname)
+ e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cert->nickname);
+ else if (cert->priv->cn)
+ e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cn);
+ else
+ e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cert->subjectName);
+
+ /* This sequence will be contain the tbsCertificate, signatureAlgorithm,
+ and signatureValue. */
+
+ if (!create_tbs_certificate_asn1_struct (cert, &sequence))
+ return FALSE;
+ e_asn1_object_append_child (cert->priv->asn1, sequence);
+ g_object_unref (sequence);
+
+ if (!process_sec_algorithm_id (&cert->priv->cert->signatureWrap.signatureAlgorithm, &sequence))
+ return FALSE;
+ e_asn1_object_set_display_name (sequence, _("Certificate Signature Algorithm"));
+ e_asn1_object_append_child (cert->priv->asn1, sequence);
+ g_object_unref (sequence);
+
+ sequence = e_asn1_object_new ();
+ e_asn1_object_set_display_name (sequence, _("Certificate Signature Value"));
+
+ /* The signatureWrap is encoded as a bit string.
+ The function ProcessRawBytes expects the
+ length to be in bytes, so let's convert the
+ length in a temporary SECItem */
+ temp.data = cert->priv->cert->signatureWrap.signature.data;
+ temp.len = cert->priv->cert->signatureWrap.signature.len / 8;
+ process_raw_bytes (&temp, &text);
+ e_asn1_object_set_display_value (sequence, text);
+ e_asn1_object_append_child (cert->priv->asn1, sequence);
+ g_free (text);
+
+ return TRUE;
+}
+
+EASN1Object*
+e_cert_get_asn1_struct (ECert *cert)
+{
+ if (!cert->priv->asn1)
+ create_asn1_struct (cert);
+
+ return g_object_ref (cert->priv->asn1);
+}
+
gboolean
e_cert_mark_for_deletion (ECert *cert)
{
diff --git a/smime/lib/e-cert.h b/smime/lib/e-cert.h
index ac596b285b..d18e0a9486 100644
--- a/smime/lib/e-cert.h
+++ b/smime/lib/e-cert.h
@@ -25,6 +25,7 @@
#include <glib-object.h>
#include <cert.h>
+#include "e-asn1-object.h"
#define E_TYPE_CERT (e_cert_get_type ())
#define E_CERT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CERT, ECert))
@@ -91,7 +92,8 @@ const char* e_cert_get_serial_number (ECert *cert);
const char* e_cert_get_sha1_fingerprint (ECert *cert);
const char* e_cert_get_md5_fingerprint (ECert *cert);
-GList* e_cert_get_chain (ECert *cert);
+GList* e_cert_get_chain (ECert *cert);
+EASN1Object* e_cert_get_asn1_struct (ECert *cert);
gboolean e_cert_mark_for_deletion (ECert *cert);