diff options
author | Stefan Walter <stefw@src.gnome.org> | 2009-01-28 01:38:38 +0000 |
---|---|---|
committer | Stefan Walter <stefw@src.gnome.org> | 2009-01-28 01:38:38 +0000 |
commit | e4f14652fa7967433f7964a8577f34cf560aa13f (patch) | |
tree | 1817d14a2a8ec0d6b62ade321fc9e25ddece7f42 | |
parent | 3f51008107b675690d705b32cf7e2637a39c0d9b (diff) |
Add basic UI widgets for displaying a certificate.
* egg/egg-asn1.c:
* egg/egg-asn1.h:
* egg/egg-hex.c:
* egg/egg-hex.h:
* egg/pkix.asn:
* egg/tests/unit-test-asn1.c:
* egg/tests/unit-test-hex.c:
* gcr/gcr-certificate.c: (added)
* gcr/gcr-certificate.h: (added)
* gcr/gcr-certificate-basics-widget.c: (added)
* gcr/gcr-certificate-basics-widget.glade: (added)
* gcr/gcr-certificate-basics-widget.h: (added)
* gcr/gcr-certificate-details-widget.c: (added)
* gcr/gcr-certificate-details-widget.h: (added)
* gcr/gcr-library.c:
* gcr/gcr-parser.c:
* gcr/Makefile.am:
* gcr/tests/Makefile.am:
* gcr/tests/ui-test-details.c: (added)
* gcr/tests/unit-test-certificate.c: (added) Add basic UI widgets for
displaying a certificate.
svn path=/trunk/; revision=1480
-rw-r--r-- | ChangeLog | 24 | ||||
-rw-r--r-- | egg/egg-asn1.c | 387 | ||||
-rw-r--r-- | egg/egg-asn1.h | 26 | ||||
-rw-r--r-- | egg/egg-hex.c | 33 | ||||
-rw-r--r-- | egg/egg-hex.h | 10 | ||||
-rw-r--r-- | egg/pkix.asn | 9 | ||||
-rw-r--r-- | egg/tests/unit-test-asn1.c | 87 | ||||
-rw-r--r-- | egg/tests/unit-test-hex.c | 16 | ||||
-rw-r--r-- | gcr/Makefile.am | 13 | ||||
-rw-r--r-- | gcr/gcr-certificate-basics-widget.c | 306 | ||||
-rw-r--r-- | gcr/gcr-certificate-basics-widget.glade | 512 | ||||
-rw-r--r-- | gcr/gcr-certificate-basics-widget.h | 57 | ||||
-rw-r--r-- | gcr/gcr-certificate-details-widget.c | 462 | ||||
-rw-r--r-- | gcr/gcr-certificate-details-widget.h | 57 | ||||
-rw-r--r-- | gcr/gcr-certificate.c | 398 | ||||
-rw-r--r-- | gcr/gcr-certificate.h | 91 | ||||
-rw-r--r-- | gcr/gcr-library.c | 3 | ||||
-rw-r--r-- | gcr/gcr-parser.c | 4 | ||||
-rw-r--r-- | gcr/tests/Makefile.am | 22 | ||||
-rw-r--r-- | gcr/tests/ui-test-details.c | 45 | ||||
-rw-r--r-- | gcr/tests/unit-test-certificate.c | 139 | ||||
-rw-r--r-- | po/ChangeLog | 7 | ||||
-rw-r--r-- | po/POTFILES.in | 5 | ||||
-rw-r--r-- | po/POTFILES.skip | 1 |
24 files changed, 2572 insertions, 142 deletions
@@ -1,3 +1,27 @@ +2009-01-27 Stef Walter <stef@memberwebs.com> + + * egg/egg-asn1.c: + * egg/egg-asn1.h: + * egg/egg-hex.c: + * egg/egg-hex.h: + * egg/pkix.asn: + * egg/tests/unit-test-asn1.c: + * egg/tests/unit-test-hex.c: + * gcr/gcr-certificate.c: (added) + * gcr/gcr-certificate.h: (added) + * gcr/gcr-certificate-basics-widget.c: (added) + * gcr/gcr-certificate-basics-widget.glade: (added) + * gcr/gcr-certificate-basics-widget.h: (added) + * gcr/gcr-certificate-details-widget.c: (added) + * gcr/gcr-certificate-details-widget.h: (added) + * gcr/gcr-library.c: + * gcr/gcr-parser.c: + * gcr/Makefile.am: + * gcr/tests/Makefile.am: + * gcr/tests/ui-test-details.c: (added) + * gcr/tests/unit-test-certificate.c: (added) Add basic UI widgets for + displaying a certificate. + 2009-01-22 Stef Walter <stef@memberwebs.com> * daemon/pk/gkr-pk-index.c: diff --git a/egg/egg-asn1.c b/egg/egg-asn1.c index 2910ca02..0390e7a5 100644 --- a/egg/egg-asn1.c +++ b/egg/egg-asn1.c @@ -29,6 +29,8 @@ #include <string.h> +#include <glib/gi18n-lib.h> + /* * HACK: asn1Parser defines these arrays as extern const, which gives * gcc a fit. So we def it out. @@ -439,25 +441,27 @@ time_t timegm(struct tm *t) } #endif //NOT_HAVE_TIMEGM -time_t -egg_asn1_parse_utc_time (const gchar *time) +static gboolean +parse_utc_time (const gchar *time, struct tm* when, gint *offset) { - struct tm when; guint n_time; - time_t result; const char *p, *e; int year; - g_assert (time); + g_assert (when); + g_assert (time); + g_assert (offset); + n_time = strlen (time); /* YYMMDDhhmmss.ffff Z | +0000 */ if (n_time < 6 || n_time >= 28) - return -1; + return FALSE; /* Reset everything to default legal values */ - memset (&when, 0, sizeof (when)); - when.tm_mday = 1; + memset (when, 0, sizeof (*when)); + *offset = 0; + when->tm_mday = 1; /* Select the digits part of it */ p = time; @@ -471,48 +475,41 @@ egg_asn1_parse_utc_time (const gchar *time) * 40 years in the past is our century. 60 years * in the future is the next century. */ - when.tm_year = two_to_four_digit_year (year) - 1900; + when->tm_year = two_to_four_digit_year (year) - 1900; } if (p + 2 <= e) { - when.tm_mon = atoin (p, 2) - 1; + when->tm_mon = atoin (p, 2) - 1; p += 2; } if (p + 2 <= e) { - when.tm_mday = atoin (p, 2); + when->tm_mday = atoin (p, 2); p += 2; } if (p + 2 <= e) { - when.tm_hour = atoin (p, 2); + when->tm_hour = atoin (p, 2); p += 2; } if (p + 2 <= e) { - when.tm_min = atoin (p, 2); + when->tm_min = atoin (p, 2); p += 2; } if (p + 2 <= e) { - when.tm_sec = atoin (p, 2); + when->tm_sec = atoin (p, 2); p += 2; } - if (when.tm_year < 0 || when.tm_year > 9999 || - when.tm_mon < 0 || when.tm_mon > 11 || - when.tm_mday < 1 || when.tm_mday > 31 || - when.tm_hour < 0 || when.tm_hour > 23 || - when.tm_min < 0 || when.tm_min > 59 || - when.tm_sec < 0 || when.tm_sec > 59) - return -1; + if (when->tm_year < 0 || when->tm_year > 9999 || + when->tm_mon < 0 || when->tm_mon > 11 || + when->tm_mday < 1 || when->tm_mday > 31 || + when->tm_hour < 0 || when->tm_hour > 23 || + when->tm_min < 0 || when->tm_min > 59 || + when->tm_sec < 0 || when->tm_sec > 59) + return FALSE; /* Make sure all that got parsed */ if (p != e) - return -1; + return FALSE; - /* In order to work with 32 bit time_t. */ - if (sizeof (time_t) <= 4 && when.tm_year >= 2038) - return (time_t) 2145914603; /* 2037-12-31 23:23:23 */ - - /* Covnvert to seconds since epoch */ - result = timegm (&when); - /* Now the remaining optional stuff */ e = time + n_time; @@ -543,81 +540,113 @@ egg_asn1_parse_utc_time (const gchar *time) /* Use TZ offset */ if (neg) - result -= off; + *offset = 0 - off; else - result += off; + *offset = off; } /* Make sure everything got parsed */ if (p != e) - return -1; + return FALSE; + return TRUE; +} + +static time_t +when_to_time (struct tm *when, gint offset) +{ + time_t result; + + g_assert (when); + + /* In order to work with 32 bit time_t. */ + if (sizeof (time_t) <= 4 && when->tm_year >= 2038) + return (time_t) 2145914603; /* 2037-12-31 23:23:23 */ + + /* Convert to seconds since epoch */ + result = timegm (when); + if (result >= 0) + result += offset; + return result; } time_t -egg_asn1_parse_general_time (const gchar *time) +egg_asn1_time_parse_utc (const gchar *time) { struct tm when; - guint n_time; - time_t result; + gint offset; + + g_return_val_if_fail (time, -1); + + if (!parse_utc_time (time, &when, &offset)) + return -1; + + return when_to_time (&when, offset); +} + +static gboolean +parse_general_time (const gchar *time, struct tm* when, gint *offset) +{ const char *p, *e; + guint n_time; - g_assert (time); + g_assert (time); + g_assert (when); + g_assert (offset); + n_time = strlen (time); /* YYYYMMDDhhmmss.ffff Z | +0000 */ if (n_time < 8 || n_time >= 30) - return -1; + return FALSE; /* Reset everything to default legal values */ - memset (&when, 0, sizeof (when)); - when.tm_mday = 1; + memset (when, 0, sizeof (*when)); + *offset = 0; + when->tm_mday = 1; /* Select the digits part of it */ p = time; for (e = p; *e >= '0' && *e <= '9'; ++e); if (p + 4 <= e) { - when.tm_year = atoin (p, 4) - 1900; + when->tm_year = atoin (p, 4) - 1900; p += 4; } if (p + 2 <= e) { - when.tm_mon = atoin (p, 2) - 1; + when->tm_mon = atoin (p, 2) - 1; p += 2; } if (p + 2 <= e) { - when.tm_mday = atoin (p, 2); + when->tm_mday = atoin (p, 2); p += 2; } if (p + 2 <= e) { - when.tm_hour = atoin (p, 2); + when->tm_hour = atoin (p, 2); p += 2; } if (p + 2 <= e) { - when.tm_min = atoin (p, 2); + when->tm_min = atoin (p, 2); p += 2; } if (p + 2 <= e) { - when.tm_sec = atoin (p, 2); + when->tm_sec = atoin (p, 2); p += 2; } - if (when.tm_year < 0 || when.tm_year > 9999 || - when.tm_mon < 0 || when.tm_mon > 11 || - when.tm_mday < 1 || when.tm_mday > 31 || - when.tm_hour < 0 || when.tm_hour > 23 || - when.tm_min < 0 || when.tm_min > 59 || - when.tm_sec < 0 || when.tm_sec > 59) - return -1; + if (when->tm_year < 0 || when->tm_year > 9999 || + when->tm_mon < 0 || when->tm_mon > 11 || + when->tm_mday < 1 || when->tm_mday > 31 || + when->tm_hour < 0 || when->tm_hour > 23 || + when->tm_min < 0 || when->tm_min > 59 || + when->tm_sec < 0 || when->tm_sec > 59) + return FALSE; /* Make sure all that got parsed */ if (p != e) - return -1; + return FALSE; - /* Covnvert to seconds since epoch */ - result = timegm (&when); - /* Now the remaining optional stuff */ e = time + n_time; @@ -648,25 +677,43 @@ egg_asn1_parse_general_time (const gchar *time) /* Use TZ offset */ if (neg) - result -= off; + *offset = 0 - off; else - result += off; + *offset = off; } /* Make sure everything got parsed */ if (p != e) - return -1; + return FALSE; - return result; + return TRUE; } -gboolean -egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val) +time_t +egg_asn1_time_parse_general (const gchar *time) { - #define MAX_TIME 1024 - gchar ttime[MAX_TIME]; + struct tm when; + gint offset; + + g_return_val_if_fail (time, -1); + + if (!parse_general_time (time, &when, &offset)) + return -1; + + return when_to_time (&when, offset); +} + +static gboolean +read_asn1_time (ASN1_TYPE asn, const gchar *part, struct tm *when, gint *offset) +{ + gchar ttime[256]; gchar *name; int len, res; + + g_assert (asn); + g_assert (part); + g_assert (when); + g_assert (offset); len = sizeof (ttime) - 1; res = asn1_read_value (asn, part, ttime, &len); @@ -681,8 +728,7 @@ egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val) g_free (name); if (res != ASN1_SUCCESS) return FALSE; - - *val = egg_asn1_parse_general_time (ttime); + return parse_general_time (ttime, when, offset); /* UTCTIME */ } else { @@ -692,16 +738,45 @@ egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val) g_free (name); if (res != ASN1_SUCCESS) return FALSE; - - *val = egg_asn1_parse_utc_time (ttime); + return parse_utc_time (ttime, when, offset); } - if (*val < (time_t)0) + return FALSE; +} + +gboolean +egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val) +{ + struct tm when; + gint offset; + + g_return_val_if_fail (asn, FALSE); + g_return_val_if_fail (part, FALSE); + g_return_val_if_fail (val, FALSE); + + if (!read_asn1_time (asn, part, &when, &offset)) return FALSE; - - return TRUE; + + *val = when_to_time (&when, offset); + return TRUE; } +gboolean +egg_asn1_read_date (ASN1_TYPE asn, const gchar *part, GDate *date) +{ + struct tm when; + gint offset; + + g_return_val_if_fail (asn, FALSE); + g_return_val_if_fail (part, FALSE); + g_return_val_if_fail (date, FALSE); + + if (!read_asn1_time (asn, part, &when, &offset)) + return FALSE; + + g_date_set_dmy (date, when.tm_mday, when.tm_mon + 1, when.tm_year + 1900); + return g_date_valid (date); +} /* ------------------------------------------------------------------------------- * Reading DN's @@ -710,42 +785,43 @@ egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val) typedef struct _PrintableOid { GQuark oid; const gchar *oidstr; - const gchar *display; + const gchar *attr; + const gchar *description; gboolean is_choice; } PrintableOid; static PrintableOid printable_oids[] = { - { 0, "0.9.2342.19200300.100.1.25", "DC", FALSE }, - { 0, "0.9.2342.19200300.100.1.1", "UID", TRUE }, - - { 0, "1.2.840.113549.1.9.1", "EMAIL", FALSE }, - { 0, "1.2.840.113549.1.9.7", NULL, TRUE }, - { 0, "1.2.840.113549.1.9.20", NULL, FALSE }, - - { 0, "1.3.6.1.5.5.7.9.1", "dateOfBirth", FALSE }, - { 0, "1.3.6.1.5.5.7.9.2", "placeOfBirth", FALSE }, - { 0, "1.3.6.1.5.5.7.9.3", "gender", FALSE }, - { 0, "1.3.6.1.5.5.7.9.4", "countryOfCitizenship", FALSE }, - { 0, "1.3.6.1.5.5.7.9.5", "countryOfResidence", FALSE }, - - { 0, "2.5.4.3", "CN", TRUE }, - { 0, "2.5.4.4", "surName", TRUE }, - { 0, "2.5.4.5", "serialNumber", FALSE }, - { 0, "2.5.4.6", "C", FALSE, }, - { 0, "2.5.4.7", "L", TRUE }, - { 0, "2.5.4.8", "ST", TRUE }, - { 0, "2.5.4.9", "STREET", TRUE }, - { 0, "2.5.4.10", "O", TRUE }, - { 0, "2.5.4.11", "OU", TRUE }, - { 0, "2.5.4.12", "T", TRUE }, - { 0, "2.5.4.20", "telephoneNumber", FALSE }, - { 0, "2.5.4.42", "givenName", TRUE }, - { 0, "2.5.4.43", "initials", TRUE }, - { 0, "2.5.4.44", "generationQualifier", TRUE }, - { 0, "2.5.4.46", "dnQualifier", FALSE }, - { 0, "2.5.4.65", "pseudonym", TRUE }, - - { 0, NULL, NULL, FALSE } + { 0, "0.9.2342.19200300.100.1.25", "DC", N_("Domain Component"), FALSE }, + { 0, "0.9.2342.19200300.100.1.1", "UID", N_("User ID"), TRUE }, + + { 0, "1.2.840.113549.1.9.1", "EMAIL", N_("Email"), FALSE }, + { 0, "1.2.840.113549.1.9.7", NULL, NULL, TRUE }, + { 0, "1.2.840.113549.1.9.20", NULL, NULL, FALSE }, + + { 0, "1.3.6.1.5.5.7.9.1", "dateOfBirth", N_("Date of Birth"), FALSE }, + { 0, "1.3.6.1.5.5.7.9.2", "placeOfBirth", N_("Place of Birth"), FALSE }, + { 0, "1.3.6.1.5.5.7.9.3", "gender", N_("Gender"), FALSE }, + { 0, "1.3.6.1.5.5.7.9.4", "countryOfCitizenship", N_("Country of Citizenship"), FALSE }, + { 0, "1.3.6.1.5.5.7.9.5", "countryOfResidence", N_("Country of Residence"), FALSE }, + + { 0, "2.5.4.3", "CN", N_("Common Name"), TRUE }, + { 0, "2.5.4.4", "surName", N_("Surname"), TRUE }, + { 0, "2.5.4.5", "serialNumber", N_("Serial Number"), FALSE }, + { 0, "2.5.4.6", "C", N_("Country"), FALSE, }, + { 0, "2.5.4.7", "L", N_("Locality"), TRUE }, + { 0, "2.5.4.8", "ST", N_("State"), TRUE }, + { 0, "2.5.4.9", "STREET", N_("Street"), TRUE }, + { 0, "2.5.4.10", "O", N_("Organization"), TRUE }, + { 0, "2.5.4.11", "OU", N_("Organizational Unit"), TRUE }, + { 0, "2.5.4.12", "T", N_("Title"), TRUE }, + { 0, "2.5.4.20", "telephoneNumber", N_("Telephone Number"), FALSE }, + { 0, "2.5.4.42", "givenName", N_("Given Name"), TRUE }, + { 0, "2.5.4.43", "initials", N_("Initials"), TRUE }, + { 0, "2.5.4.44", "generationQualifier", N_("Generation Qualifier"), TRUE }, + { 0, "2.5.4.46", "dnQualifier", N_("DN Qualifier"), FALSE }, + { 0, "2.5.4.65", "pseudonym", N_("Pseudonym"), TRUE }, + + { 0, NULL, NULL, NULL, FALSE } }; static void @@ -794,7 +870,7 @@ dn_print_hex_value (const guchar *data, gsize len) } static gchar* -dn_print_oid_value_parsed (PrintableOid *printable, guchar *data, gsize len) +dn_print_oid_value_parsed (PrintableOid *printable, const guchar *data, gsize len) { const gchar *asn_name; ASN1_TYPE asn1; @@ -857,7 +933,7 @@ dn_print_oid_value_parsed (PrintableOid *printable, guchar *data, gsize len) } static gchar* -dn_print_oid_value (PrintableOid *printable, guchar *data, gsize len) +dn_print_oid_value (PrintableOid *printable, const guchar *data, gsize len) { gchar *value; @@ -903,7 +979,7 @@ dn_parse_rdn (ASN1_TYPE asn, const gchar *part) g_return_val_if_fail (value, NULL); display = dn_print_oid_value (printable, value, n_value); - result = g_strconcat (printable ? printable->display : g_quark_to_string (oid), + result = g_strconcat (printable && printable->attr ? printable->attr : g_quark_to_string (oid), "=", display, NULL); g_free (display); @@ -992,8 +1068,8 @@ egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match) /* Does it match either the OID or the displayable? */ if (g_ascii_strcasecmp (g_quark_to_string (oid), match) != 0) { printable = dn_find_printable (oid); - if (!printable || !printable->display || - !g_ascii_strcasecmp (printable->display, match) == 0) + if (!printable || !printable->attr || + !g_ascii_strcasecmp (printable->attr, match) == 0) continue; } @@ -1010,3 +1086,98 @@ egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match) return NULL; } + +gboolean +egg_asn1_dn_parse (ASN1_TYPE asn, const gchar *part, + EggAsn1DnCallback callback, gpointer user_data) +{ + gboolean done = FALSE; + gchar *path; + guchar *value; + gsize n_value; + GQuark oid; + guint i, j; + + g_return_val_if_fail (asn, FALSE); + + init_printable_oids (); + + /* Each (possibly multi valued) RDN */ + for (i = 1; !done; ++i) { + + /* Each type=value pair of an RDN */ + for (j = 1; TRUE; ++j) { + + /* Dig out the type */ + path = g_strdup_printf ("%s%s?%u.?%u.type", + part ? part : "", + part ? "." : "", i, j); + oid = egg_asn1_read_oid (asn, path); + g_free (path); + + if (!oid) { + done = j == 1; + break; + } + + /* Print the value as nicely as we can */ + path = g_strdup_printf ("%s%s?%u.?%u.value", + part ? part : "", + part ? "." : "", i, j); + value = egg_asn1_read_value (asn, path, &n_value, NULL); + g_free (path); + + if (!value) { + done = j == 1; + break; + } + + if (callback) + (callback) (i, oid, value, n_value, user_data); + + g_free (value); + } + } + + return i > 1; +} + +const gchar* +egg_asn1_dn_oid_attr (GQuark oid) +{ + PrintableOid *printable; + + g_return_val_if_fail (oid, NULL); + + printable = dn_find_printable (oid); + if (!printable) + return g_quark_to_string (oid); + + return printable->attr; +} + +const gchar* +egg_asn1_dn_oid_desc (GQuark oid) +{ + PrintableOid *printable; + + g_return_val_if_fail (oid, NULL); + + printable = dn_find_printable (oid); + if (!printable) + return g_quark_to_string (oid); + + return gettext (printable->description); +} + +gchar* +egg_asn1_dn_print_value (GQuark oid, const guchar *value, gsize n_value) +{ + PrintableOid *printable; + + g_return_val_if_fail (oid, NULL); + g_return_val_if_fail (value || !n_value, NULL); + + printable = dn_find_printable (oid); + return dn_print_oid_value (printable, value, n_value); +} diff --git a/egg/egg-asn1.h b/egg/egg-asn1.h index a6bfc73c..ab1d3414 100644 --- a/egg/egg-asn1.h +++ b/egg/egg-asn1.h @@ -28,7 +28,7 @@ #include <libtasn1.h> -typedef void* (*EggAllocator) (void* p, unsigned long len); +typedef void* (*EggAllocator) (void* p, gsize); ASN1_TYPE egg_asn1_get_pk_asn1type (void); @@ -56,6 +56,8 @@ gboolean egg_asn1_read_uint (ASN1_TYPE asn, const gboolean egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val); +gboolean egg_asn1_read_date (ASN1_TYPE asn, const gchar *part, GDate *date); + const guchar* egg_asn1_read_content (ASN1_TYPE asn, const guchar *data, gsize n_data, const gchar *part, gsize *n_content); @@ -64,16 +66,30 @@ const guchar* egg_asn1_read_element (ASN1_TYPE asn, const gboolean egg_asn1_write_uint (ASN1_TYPE asn, const gchar *part, guint val); +gint egg_asn1_element_length (const guchar *data, gsize n_data); + +const guchar* egg_asn1_element_content (const guchar *data, gsize n_data, gsize *n_content); + gchar* egg_asn1_read_dn (ASN1_TYPE asn, const gchar *part); gchar* egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match); -gint egg_asn1_element_length (const guchar *data, gsize n_data); -const guchar* egg_asn1_element_content (const guchar *data, gsize n_data, gsize *n_content); +glong egg_asn1_time_parse_utc (const gchar* value); + +glong egg_asn1_time_parse_general (const gchar* value); + + +typedef void (*EggAsn1DnCallback) (guint index, GQuark oid, const guchar *value, + gsize n_value, gpointer user_data); + +gboolean egg_asn1_dn_parse (ASN1_TYPE asn, const gchar *part, + EggAsn1DnCallback callback, gpointer user_data); + +const gchar* egg_asn1_dn_oid_attr (GQuark oid); -glong egg_asn1_parse_utc_time (const gchar* value); +const gchar* egg_asn1_dn_oid_desc (GQuark oid); -glong egg_asn1_parse_general_time (const gchar* value); +gchar* egg_asn1_dn_print_value (GQuark oid, const guchar *value, gsize n_value); #endif /*EGG_ASN1_H_*/ diff --git a/egg/egg-hex.c b/egg/egg-hex.c index cdfc33e8..50346606 100644 --- a/egg/egg-hex.c +++ b/egg/egg-hex.c @@ -81,24 +81,37 @@ egg_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded) gchar* egg_hex_encode (const guchar *data, gsize n_data) { - gchar *result, *encoded; + return egg_hex_encode_full (data, n_data, 0); +} + +gchar* +egg_hex_encode_full (const guchar *data, gsize n_data, guint group) +{ + GString *result; + gsize bytes; guchar j; g_return_val_if_fail (data || !n_data, NULL); + + result = g_string_sized_new (n_data * 2 + 1); + bytes = 0; - encoded = result = g_malloc0 (n_data * 2 + 1); - - while(n_data > 0) { + while (n_data > 0) { + + if (group && bytes && (bytes % group) == 0) + g_string_append_c (result, ' '); + j = *(data) >> 4 & 0xf; - *(encoded++) = HEXC[j]; - + g_string_append_c (result, HEXC[j]); + j = *(data++) & 0xf; - *(encoded++) = HEXC[j]; + g_string_append_c (result, HEXC[j]); - n_data--; + ++bytes; + --n_data; } /* Make sure still null terminated */ - g_assert (encoded[n_data * 2] == 0); - return result; + return g_string_free (result, FALSE); } + diff --git a/egg/egg-hex.h b/egg/egg-hex.h index ddcd9238..fa53e56d 100644 --- a/egg/egg-hex.h +++ b/egg/egg-hex.h @@ -25,10 +25,14 @@ #include <glib.h> guchar* egg_hex_decode (const gchar *data, - gssize n_data, - gsize *n_decoded); + gssize n_data, + gsize *n_decoded); gchar* egg_hex_encode (const guchar *data, - gsize n_data); + gsize n_data); + +gchar* egg_hex_encode_full (const guchar *data, + gsize n_data, + guint group); #endif /* EGG_HEX_H_ */ diff --git a/egg/pkix.asn b/egg/pkix.asn index 1e5d6571..c220c8af 100644 --- a/egg/pkix.asn +++ b/egg/pkix.asn @@ -1195,15 +1195,14 @@ pkcs-7-EncryptedContent ::= OCTET STRING pkcs-7-UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute -- LDAP stuff --- may not be correct -id-at-ldap-DC AttributeType ::= { 0 9 2342 19200300 100 1 25 } +id-at-domainComponent AttributeType ::= { 0 9 2342 19200300 100 1 25 } -ldap-DC ::= IA5String +domainComponent ::= IA5String -id-at-ldap-UID AttributeType ::= { 0 9 2342 19200300 100 1 1 } +id-at-userId AttributeType ::= { 0 9 2342 19200300 100 1 1 } -ldap-UID ::= DirectoryString +userId ::= DirectoryString -- rfc3039 diff --git a/egg/tests/unit-test-asn1.c b/egg/tests/unit-test-asn1.c index f4cc44fe..818869df 100644 --- a/egg/tests/unit-test-asn1.c +++ b/egg/tests/unit-test-asn1.c @@ -331,7 +331,7 @@ DEFINE_TEST(general_time) const TimeTestData *data; for (data = generalized_time_test_data; data->value; ++data) { - when = egg_asn1_parse_general_time (data->value); + when = egg_asn1_time_parse_general (data->value); if (data->ref != when) { printf ("%s", data->value); printf ("%s != ", ctime (&when)); @@ -349,7 +349,7 @@ DEFINE_TEST(utc_time) const TimeTestData *data; for (data = utc_time_test_data; data->value; ++data) { - when = egg_asn1_parse_utc_time (data->value); + when = egg_asn1_time_parse_utc (data->value); if (data->ref != when) { printf ("%s", data->value); printf ("%s != ", ctime (&when)); @@ -370,6 +370,16 @@ DEFINE_TEST(read_time) g_assert_cmpint (time, ==, 820454400); } +DEFINE_TEST(read_date) +{ + GDate date; + if (!egg_asn1_read_date (asn1_cert, "tbsCertificate.validity.notAfter", &date)) + g_assert_not_reached (); + g_assert_cmpint (date.day, ==, 31); + g_assert_cmpint (date.month, ==, 12); + g_assert_cmpint (date.year, ==, 2020); +} + DEFINE_TEST(read_dn) { gchar *dn; @@ -384,6 +394,79 @@ DEFINE_TEST(read_dn) g_assert (dn == NULL); } +DEFINE_TEST(dn_oid) +{ + GQuark oid; + + oid = g_quark_from_static_string ("0.9.2342.19200300.100.1.25"); + g_assert_cmpstr (egg_asn1_dn_oid_attr (oid), ==, "DC"); + g_assert_cmpstr (egg_asn1_dn_oid_desc (oid), ==, "Domain Component"); + + /* Should return OID for invalid oids */ + oid = g_quark_from_static_string ("1.1.1.1.1"); + g_assert_cmpstr (egg_asn1_dn_oid_attr (oid), ==, "1.1.1.1.1"); + g_assert_cmpstr (egg_asn1_dn_oid_desc (oid), ==, "1.1.1.1.1"); +} + +DEFINE_TEST(dn_value) +{ + const guchar value[] = { 0x13, 0x1a, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20, 0x43, 0x41 }; + gsize n_value = 28; + GQuark oid; + gchar *text; + + /* Some printable strings */ + oid = g_quark_from_static_string ("2.5.4.3"); + text = egg_asn1_dn_print_value (oid, value, n_value); + g_assert_cmpstr (text, ==, "Thawte Personal Premium CA"); + g_free (text); + + /* Unknown oid */ + oid = g_quark_from_static_string ("1.1.1.1.1.1"); + text = egg_asn1_dn_print_value (oid, value, n_value); + g_assert_cmpstr (text, ==, "#131A54686177746520506572736F6E616C205072656D69756D204341"); + g_free (text); +} + +static int last_index = 0; + +static void +concatenate_dn (guint index, GQuark oid, const guchar *value, gsize n_value, gpointer user_data) +{ + GString *dn = user_data; + gchar *text; + + g_assert (oid); + g_assert (value); + g_assert (n_value); + + g_assert (index == last_index); + ++last_index; + + if (index != 1) { + g_string_append (dn, ", "); + } + + g_string_append (dn, egg_asn1_dn_oid_attr (oid)); + g_string_append_c (dn, '='); + + text = egg_asn1_dn_print_value (oid, value, n_value); + g_string_append (dn, text); + g_free (text); +} + +DEFINE_TEST(parse_dn) +{ + GString *dn = g_string_new (""); + last_index = 1; + + if (!egg_asn1_dn_parse (asn1_cert, "tbsCertificate.issuer.rdnSequence", concatenate_dn, dn)) + g_assert_not_reached (); + + g_assert_cmpstr (dn->str, ==, "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting, OU=Certification Services Division, CN=Thawte Personal Premium CA, EMAIL=personal-premium@thawte.com"); + g_string_free (dn, TRUE); +} + DEFINE_TEST(read_dn_part) { gchar *value; diff --git a/egg/tests/unit-test-hex.c b/egg/tests/unit-test-hex.c index 1664ef71..5b9ece65 100644 --- a/egg/tests/unit-test-hex.c +++ b/egg/tests/unit-test-hex.c @@ -31,6 +31,7 @@ static const guchar TEST_DATA[] = { 0x05, 0xD6, 0x95, 0x96, 0x10, 0x12, 0xAE, 0x35 }; static const gchar *TEST_HEX = "05D695961012AE35"; +static const gchar *TEST_HEX_DELIM = "05 D6 95 96 10 12 AE 35"; static const gchar *TEST_HEX_SPACE = "\n05 D695 \r961012AE35\n\n"; DEFINE_TEST(hex_encode) @@ -42,6 +43,21 @@ DEFINE_TEST(hex_encode) g_assert_cmpstr (hex, ==, TEST_HEX); } +DEFINE_TEST(hex_encode_spaces) +{ + gchar *hex; + + /* Encode without spaces */ + hex = egg_hex_encode_full (TEST_DATA, sizeof (TEST_DATA), 0); + g_assert (hex); + g_assert_cmpstr (hex, ==, TEST_HEX); + + /* Encode without spaces */ + hex = egg_hex_encode_full (TEST_DATA, sizeof (TEST_DATA), 1); + g_assert (hex); + g_assert_cmpstr (hex, ==, TEST_HEX_DELIM); +} + DEFINE_TEST(hex_decode) { guchar *data; diff --git a/gcr/Makefile.am b/gcr/Makefile.am index c9de7506..44498ea9 100644 --- a/gcr/Makefile.am +++ b/gcr/Makefile.am @@ -5,6 +5,7 @@ uidir = $(datadir)/gcr/ui/ GLADE_FILES = \ + gcr-certificate-basics-widget.glade \ gcr-import-dialog.glade .glade.ui: @@ -20,7 +21,9 @@ INCLUDES = \ -I$(top_srcdir) \ $(GTK_CFLAGS) \ $(GOBJECT_CFLAGS) \ - $(GLIB_CFLAGS) + $(GLIB_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) \ + $(LIBTASN1_CFLAGS) BUILT_SOURCES = \ gcr-marshal.c gcr-marshal.h @@ -28,6 +31,9 @@ BUILT_SOURCES = \ lib_LTLIBRARIES = libgcr.la libgcr_la_SOURCES = \ + gcr-certificate.c gcr-certificate.h \ + gcr-certificate-basics-widget.c gcr-certificate-basics-widget.h \ + gcr-certificate-details-widget.c gcr-certificate-details-widget.h \ gcr-import-dialog.c gcr-import-dialog.h \ gcr-importer.c gcr-importer.h \ gcr-internal.h \ @@ -50,7 +56,9 @@ libgcr_la_LIBADD = \ $(top_builddir)/egg/libegg-secure-entry.la \ $(top_builddir)/gp11/libgp11.la \ $(GOBJECT_LIBS) \ - $(GLIB_LIBS) + $(GLIB_LIBS) \ + $(LIBGCRYPT_LIBS) \ + $(LIBTASN1_LIBS) gcr-marshal.h: gcr-marshal.list $(GLIB_GENMARSHAL) $(GLIB_GENMARSHAL) $< --header --prefix=_gcr_marshal > $@ @@ -70,7 +78,6 @@ gcr-$(GCR_MAJOR).pc: gcr.pc EXTRA_DIST = \ gcr.pc.in \ gcr-marshal.list \ - gcr-import-dialog.glade \ $(GLADE_FILES) CLEANFILES = \ diff --git a/gcr/gcr-certificate-basics-widget.c b/gcr/gcr-certificate-basics-widget.c new file mode 100644 index 00000000..5bd91c98 --- /dev/null +++ b/gcr/gcr-certificate-basics-widget.c @@ -0,0 +1,306 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "gcr-certificate.h" +#include "gcr-certificate-basics-widget.h" + +#include <glib/gi18n-lib.h> + +enum { + PROP_0, + PROP_CERTIFICATE +}; + +struct _GcrCertificateBasicsWidgetPrivate { + GcrCertificate *certificate; + GtkBuilder *builder; +}; + +G_DEFINE_TYPE (GcrCertificateBasicsWidget, gcr_certificate_basics_widget, GTK_TYPE_ALIGNMENT); + +/* ----------------------------------------------------------------------------- + * INTERNAL + */ + +static void +set_certificate_part_label (GtkBuilder *builder, const char *name, const gchar *value) +{ + GtkWidget *widget; + gchar *markup; + + widget = GTK_WIDGET (gtk_builder_get_object (builder, name)); + g_return_if_fail (GTK_IS_LABEL (widget)); + if(value) + { + markup = g_markup_escape_text (value, -1); + gtk_label_set_markup (GTK_LABEL (widget), markup); + g_free (markup); + } + else + { + gtk_label_set_markup (GTK_LABEL (widget), _("<i>Not Part of Certificate</i>")); + } +} + +static void +set_certificate_part_date (GtkBuilder *builder, const char *name, const GDate *value) +{ + GtkWidget *widget; + gchar *formatted; + + widget = GTK_WIDGET (gtk_builder_get_object (builder, name)); + g_return_if_fail (GTK_IS_LABEL (widget)); + if(value) + { + formatted = g_new (gchar, 11); + g_date_strftime (formatted, 11, "%Y-%m-%d", value); + gtk_label_set_text (GTK_LABEL (widget), formatted); + g_free (formatted); + } + else + { + gtk_label_set_markup (GTK_LABEL (widget), _("<i>unknown</i>")); + } +} + +static void +refresh_display (GcrCertificateBasicsWidget *self) +{ + gchar *value; + GDate *date; + + /* Issued To / Subject */ + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_subject_cn (self->pv->certificate); + set_certificate_part_label (self->pv->builder, "issued-to-cn", value); + g_free (value); + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_subject_part (self->pv->certificate, "o"); + set_certificate_part_label (self->pv->builder, "issued-to-o", value); + g_free (value); + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_subject_part (self->pv->certificate, "ou"); + set_certificate_part_label (self->pv->builder, "issued-to-ou", value); + g_free (value); + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_serial_number_hex (self->pv->certificate); + set_certificate_part_label (self->pv->builder, "issued-to-serial", value); + g_free (value); + + + /* Issued By / Issuer */ + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_issuer_cn (self->pv->certificate); + set_certificate_part_label (self->pv->builder, "issued-by-cn", value); + g_free (value); + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_issuer_part (self->pv->certificate, "o"); + set_certificate_part_label (self->pv->builder, "issued-by-o", value); + g_free (value); + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_issuer_part (self->pv->certificate, "ou"); + set_certificate_part_label (self->pv->builder, "issued-by-ou", value); + g_free (value); + + + /* Expiry */ + + date = NULL; + if (self->pv->certificate) + date = gcr_certificate_get_issued_date (self->pv->certificate); + set_certificate_part_date (self->pv->builder, "validity-issued-on", date); + if (date) + g_date_free (date); + + date = NULL; + if (self->pv->certificate) + date = gcr_certificate_get_expiry_date (self->pv->certificate); + set_certificate_part_date (self->pv->builder, "validity-expires-on", date); + if (date) + g_date_free (date); + + + /* Fingerprints */ + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_fingerprint_hex (self->pv->certificate, G_CHECKSUM_SHA1); + set_certificate_part_label (self->pv->builder, "fingerprints-sha1", value); + g_free (value); + + value = NULL; + if (self->pv->certificate) + value = gcr_certificate_get_fingerprint_hex (self->pv->certificate, G_CHECKSUM_SHA1); + set_certificate_part_label (self->pv->builder, "fingerprints-md5", value); + g_free (value); +} + +/* ----------------------------------------------------------------------------- + * OBJECT + */ + + +static GObject* +gcr_certificate_basics_widget_constructor (GType type, guint n_props, GObjectConstructParam *props) +{ + GObject *obj = G_OBJECT_CLASS (gcr_certificate_basics_widget_parent_class)->constructor (type, n_props, props); + GcrCertificateBasicsWidget *self = NULL; + GtkWidget *widget; + + if (obj) { + self = GCR_CERTIFICATE_BASICS_WIDGET (obj); + + if (!gtk_builder_add_from_file (self->pv->builder, UIDIR "gcr-certificate-basics-widget.ui", NULL)) + g_return_val_if_reached (obj); + + widget = GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "certificate-basics-widget")); + g_return_val_if_fail (GTK_IS_WIDGET (widget), obj); + gtk_container_add (GTK_CONTAINER (self), widget); + gtk_widget_show (widget); + } + + return obj; +} + +static void +gcr_certificate_basics_widget_init (GcrCertificateBasicsWidget *self) +{ + self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidgetPrivate)); + self->pv->builder = gtk_builder_new (); +} + +static void +gcr_certificate_basics_widget_dispose (GObject *obj) +{ + GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj); + + if (self->pv->certificate) + g_object_unref (self->pv->certificate); + self->pv->certificate = NULL; + + G_OBJECT_CLASS (gcr_certificate_basics_widget_parent_class)->dispose (obj); +} + +static void +gcr_certificate_basics_widget_finalize (GObject *obj) +{ + GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj); + + g_assert (!self->pv->certificate); + + G_OBJECT_CLASS (gcr_certificate_basics_widget_parent_class)->finalize (obj); +} + +static void +gcr_certificate_basics_widget_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj); + + switch (prop_id) { + case PROP_CERTIFICATE: + gcr_certificate_basics_widget_set_certificate (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_basics_widget_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj); + + switch (prop_id) { + case PROP_CERTIFICATE: + g_value_set_object (value, self->pv->certificate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_basics_widget_class_init (GcrCertificateBasicsWidgetClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gcr_certificate_basics_widget_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, sizeof (GcrCertificateBasicsWidgetPrivate)); + + gobject_class->constructor = gcr_certificate_basics_widget_constructor; + gobject_class->dispose = gcr_certificate_basics_widget_dispose; + gobject_class->finalize = gcr_certificate_basics_widget_finalize; + gobject_class->set_property = gcr_certificate_basics_widget_set_property; + gobject_class->get_property = gcr_certificate_basics_widget_get_property; + + g_object_class_install_property (gobject_class, PROP_CERTIFICATE, + g_param_spec_object("certificate", "Certificate", "Certificate to display.", + GCR_TYPE_CERTIFICATE, G_PARAM_READWRITE)); +} + +/* ----------------------------------------------------------------------------- + * PUBLIC + */ + +GcrCertificateBasicsWidget* +gcr_certificate_basics_widget_new (GcrCertificate *certificate) +{ + return g_object_new (GCR_TYPE_CERTIFICATE_BASICS_WIDGET, "certificate", certificate, NULL); +} + +GcrCertificate* +gcr_certificate_basics_widget_get_certificate (GcrCertificateBasicsWidget *self) +{ + g_return_val_if_fail (GCR_IS_CERTIFICATE_BASICS_WIDGET (self), NULL); + return self->pv->certificate; +} + +void +gcr_certificate_basics_widget_set_certificate (GcrCertificateBasicsWidget *self, GcrCertificate *cert) +{ + g_return_if_fail (GCR_IS_CERTIFICATE_BASICS_WIDGET (self)); + + if (self->pv->certificate) + g_object_unref (self->pv->certificate); + self->pv->certificate = cert; + if (self->pv->certificate) + g_object_ref (self->pv->certificate); + + refresh_display (self); + g_object_notify (G_OBJECT (self), "certificate"); +} diff --git a/gcr/gcr-certificate-basics-widget.glade b/gcr/gcr-certificate-basics-widget.glade new file mode 100644 index 00000000..1df6cdee --- /dev/null +++ b/gcr/gcr-certificate-basics-widget.glade @@ -0,0 +1,512 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkWindow" id="unused-window"> + <child> + <widget class="GtkVBox" id="certificate-basics-widget"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="spacing">6</property> + <child> + <widget class="GtkFrame" id="frame1"> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <property name="border_width">6</property> + <child> + <widget class="GtkLabel" id="ssl-client-cert-verified-label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">SSL Client Certificate</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="ssl-server-cert-verified-label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">SSL Server Certificate</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="email-signer-cert-verified-label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Email Signer Certificate</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="email-recipient-cert-verified-label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Email Recipient Certificate</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>This certificate has been verified for the following uses:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator1"> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table3"> + <property name="visible">True</property> + <property name="border_width">3</property> + <property name="n_rows">15</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <child> + <widget class="GtkLabel" id="label25"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">1</property> + <property name="ypad">3</property> + <property name="label" translatable="yes"><b>Issued To</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label26"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Common Name (CN)</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label27"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Organization (O)</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label28"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Organizational Unit (OU)</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label29"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Serial Number</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label31"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Common Name (CN)</property> + </widget> + <packing> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label32"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Organization (O)</property> + </widget> + <packing> + <property name="top_attach">7</property> + <property name="bottom_attach">8</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label33"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Organizational Unit (OU)</property> + </widget> + <packing> + <property name="top_attach">8</property> + <property name="bottom_attach">9</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label36"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Issued On</property> + </widget> + <packing> + <property name="top_attach">10</property> + <property name="bottom_attach">11</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label37"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">Expires On</property> + </widget> + <packing> + <property name="top_attach">11</property> + <property name="bottom_attach">12</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label30"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">1</property> + <property name="ypad">3</property> + <property name="label" translatable="yes"><b>Issued By</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label35"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="ypad">3</property> + <property name="label" translatable="yes"><b>Fingerprints</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">12</property> + <property name="bottom_attach">13</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label38"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">SHA1 Fingerprint</property> + </widget> + <packing> + <property name="top_attach">13</property> + <property name="bottom_attach">14</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label39"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">6</property> + <property name="label" translatable="yes">MD5 Fingerprint</property> + </widget> + <packing> + <property name="top_attach">14</property> + <property name="bottom_attach">15</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-to-o"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-to-ou"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-to-serial"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-by-cn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-by-o"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">7</property> + <property name="bottom_attach">8</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-by-ou"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">8</property> + <property name="bottom_attach">9</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="validity-issued-on"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">10</property> + <property name="bottom_attach">11</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="validity-expires-on"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">11</property> + <property name="bottom_attach">12</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="fingerprints-sha1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">13</property> + <property name="bottom_attach">14</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="fingerprints-md5"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">14</property> + <property name="bottom_attach">15</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="issued-to-cn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><Not Part of Certificate></property> + <property name="selectable">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label34"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="ypad">3</property> + <property name="label" translatable="yes"><b>Validity</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">9</property> + <property name="bottom_attach">10</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gcr/gcr-certificate-basics-widget.h b/gcr/gcr-certificate-basics-widget.h new file mode 100644 index 00000000..cf000617 --- /dev/null +++ b/gcr/gcr-certificate-basics-widget.h @@ -0,0 +1,57 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GCR_CERTIFICATE_BASICS_WIDGET_H__ +#define __GCR_CERTIFICATE_BASICS_WIDGET_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#include "gcr-certificate.h" + +#define GCR_TYPE_CERTIFICATE_BASICS_WIDGET (gcr_certificate_basics_widget_get_type ()) +#define GCR_CERTIFICATE_BASICS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidget)) +#define GCR_CERTIFICATE_BASICS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidgetClass)) +#define GCR_IS_CERTIFICATE_BASICS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_BASICS_WIDGET)) +#define GCR_IS_CERTIFICATE_BASICS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_BASICS_WIDGET)) +#define GCR_CERTIFICATE_BASICS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidgetClass)) + +typedef struct _GcrCertificateBasicsWidget GcrCertificateBasicsWidget; +typedef struct _GcrCertificateBasicsWidgetClass GcrCertificateBasicsWidgetClass; +typedef struct _GcrCertificateBasicsWidgetPrivate GcrCertificateBasicsWidgetPrivate; + +struct _GcrCertificateBasicsWidget { + GtkAlignment parent; + GcrCertificateBasicsWidgetPrivate *pv; +}; + +struct _GcrCertificateBasicsWidgetClass { + GtkAlignmentClass parent_class; +}; + +GType gcr_certificate_basics_widget_get_type (void); + +GcrCertificateBasicsWidget* gcr_certificate_basics_widget_new (GcrCertificate *cert); + +GcrCertificate* gcr_certificate_basics_widget_get_certificate (GcrCertificateBasicsWidget *basics); + +void gcr_certificate_basics_widget_set_certificate (GcrCertificateBasicsWidget *basics, + GcrCertificate *cert); + +#endif /* __GCR_CERTIFICATE_BASICS_WIDGET_H__ */ diff --git a/gcr/gcr-certificate-details-widget.c b/gcr/gcr-certificate-details-widget.c new file mode 100644 index 00000000..09f80553 --- /dev/null +++ b/gcr/gcr-certificate-details-widget.c @@ -0,0 +1,462 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "gcr-certificate.h" +#include "gcr-certificate-details-widget.h" + +#include "egg/egg-asn1.h" +#include "egg/egg-hex.h" + +#include <glib/gi18n-lib.h> + +enum { + PROP_0, + PROP_CERTIFICATE +}; + +struct _GcrCertificateDetailsWidgetPrivate { + GcrCertificate *certificate; + GtkTextView *view; + GtkTextBuffer *buffer; + GtkTextTag *field_tag; + gint field_width; +}; + +G_DEFINE_TYPE (GcrCertificateDetailsWidget, gcr_certificate_details_widget, GTK_TYPE_ALIGNMENT); + +#define FIELD_MARGIN 17 +#define COLUMN_MARGIN 6 + +/* ----------------------------------------------------------------------------- + * INTERNAL + */ + +static GtkTextTagTable* +create_tag_table (GcrCertificateDetailsWidget *self) +{ + GtkTextTagTable *tags; + GtkTextTag *tag; + + g_assert (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self)); + + tags = gtk_text_tag_table_new (); + + tag = g_object_new (GTK_TYPE_TEXT_TAG, + "name", "heading", + "left-margin", 5, + "right-margin", 5, + "pixels-above-lines", 9, + "pixels-below-lines", 3, + "weight", PANGO_WEIGHT_BOLD, + NULL); + + gtk_text_tag_table_add (tags, tag); + g_object_unref (tag); + + tag = g_object_new (GTK_TYPE_TEXT_TAG, + "name", "monospace", + "family", "monospace", + NULL); + + gtk_text_tag_table_add (tags, tag); + g_object_unref (tag); + + g_assert (!self->pv->field_tag); + self->pv->field_width = 0; + self->pv->field_tag = g_object_new (GTK_TYPE_TEXT_TAG, + "name", "field", + "left-margin", self->pv->field_width + FIELD_MARGIN, + "indent", self->pv->field_width, + "right-margin", 5, + "pixels-below-lines", 3, + "wrap-mode", GTK_WRAP_WORD_CHAR, + NULL); + gtk_text_tag_table_add (tags, self->pv->field_tag); + + return tags; +} + +static void +append_field_and_value (GcrCertificateDetailsWidget *self, const gchar *field, + const gchar *value, gboolean monospace) +{ + PangoRectangle extents; + PangoTabArray *tabs; + PangoLayout *layout; + GtkTextIter iter; + gchar *text; + + text = g_strdup_printf ("%s:", field); + + /* Measure the width of the field */ + layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), text); + pango_layout_get_extents (layout, NULL, &extents); + pango_extents_to_pixels (&extents, NULL); + g_object_unref (layout); + + /* Make the tab wide enough to accomodate */ + if (extents.width > self->pv->field_width) { + self->pv->field_width = extents.width + COLUMN_MARGIN; + tabs = pango_tab_array_new (1, TRUE); + pango_tab_array_set_tab (tabs, 0, PANGO_TAB_LEFT, self->pv->field_width); + g_object_set (self->pv->field_tag, + "left-margin", FIELD_MARGIN, + "indent", 0 - self->pv->field_width, + "tabs", tabs, + NULL); + pango_tab_array_free (tabs); + } + + gtk_text_buffer_get_end_iter (self->pv->buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (self->pv->buffer, &iter, text, -1, "field", NULL); + gtk_text_buffer_insert (self->pv->buffer, &iter, "\t", 1); + gtk_text_buffer_insert_with_tags_by_name (self->pv->buffer, &iter, value, -1, "field", + monospace ? "monospace" : NULL, NULL); + gtk_text_buffer_insert (self->pv->buffer, &iter, "\n", 1); + + g_free (text); +} + +static void +append_heading (GcrCertificateDetailsWidget *self, const gchar *heading) +{ + GtkTextIter iter; + + gtk_text_buffer_get_end_iter (self->pv->buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (self->pv->buffer, &iter, heading, -1, "heading", NULL); + gtk_text_buffer_insert (self->pv->buffer, &iter, "\n", 1); +} + +static void +append_fingerprint (GcrCertificateDetailsWidget *self, const guchar *data, + gsize n_data, const gchar *name, GChecksumType type) +{ + GChecksum *checksum; + guint8 *buffer; + gsize n_buffer; + gchar *display; + + checksum = g_checksum_new (type); + g_return_if_fail (checksum); + g_checksum_update (checksum, data, n_data); + + n_buffer = g_checksum_type_get_length (type); + g_return_if_fail (n_buffer); + buffer = g_malloc0 (n_buffer); + + g_checksum_get_digest (checksum, buffer, &n_buffer); + g_checksum_free (checksum); + + display = egg_hex_encode_full (buffer, n_buffer, 1); + append_field_and_value (self, name, display, TRUE); + g_free (display); + + g_free (buffer); +} + +static void +on_parsed_dn_part (guint index, GQuark oid, const guchar *value, + gsize n_value, gpointer user_data) +{ + GcrCertificateDetailsWidget *self = user_data; + const gchar *attr; + const gchar *desc; + gchar *field; + gchar *display; + + g_return_if_fail (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self)); + + attr = egg_asn1_dn_oid_attr (oid); + desc = egg_asn1_dn_oid_desc (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_asn1_dn_print_value (oid, value, n_value); + if (display == NULL) + display = ""; + + append_field_and_value (self, field, display, FALSE); + g_free (field); + g_free (display); +} + +static void +refresh_display (GcrCertificateDetailsWidget *self) +{ + GtkTextIter start, iter; + const guchar *data, *value; + gsize n_data, n_value; + guint version; + gchar *display; + ASN1_TYPE asn; + GDate date; + + gtk_text_buffer_get_start_iter (self->pv->buffer, &start); + gtk_text_buffer_get_end_iter (self->pv->buffer, &iter); + gtk_text_buffer_delete (self->pv->buffer, &start, &iter); + + if (!self->pv->certificate) + return; + + data = gcr_certificate_get_der_data (self->pv->certificate, &n_data); + g_return_if_fail (data); + + asn = egg_asn1_decode ("PKIX1.Certificate", data, n_data); + g_return_if_fail (asn); + + /* The subject */ + append_heading (self, _("Subject Name")); + egg_asn1_dn_parse (asn, "tbsCertificate.subject.rdnSequence", on_parsed_dn_part, self); + + /* The Issuer */ + append_heading (self, _("Issuer Name")); + egg_asn1_dn_parse (asn, "tbsCertificate.issuer.rdnSequence", on_parsed_dn_part, self); + + /* The Issued Parameters */ + append_heading (self, _("Issued Certificate")); + + if (!egg_asn1_read_uint (asn, "tbsCertificate.version", &version)) + g_return_if_reached (); + display = g_strdup_printf ("%u", version + 1); + append_field_and_value (self, _("Version"), display, FALSE); + g_free (display); + + value = egg_asn1_read_content (asn, data, n_data, "tbsCertificate.serialNumber", &n_value); + g_return_if_fail (value); + display = egg_hex_encode_full (value, n_value, 1); + append_field_and_value (self, _("Serial Number"), display, TRUE); + g_free (display); + + display = g_malloc0 (128); + if (egg_asn1_read_date (asn, "tbsCertificate.validity.notBefore", &date)) { + if (!g_date_strftime (display, 128, "%Y-%m-%d", &date)) + g_return_if_reached (); + append_field_and_value (self, _("Not Valid Before"), display, FALSE); + } + if (egg_asn1_read_date (asn, "tbsCertificate.validity.notAfter", &date)) { + if (!g_date_strftime (display, 128, "%Y-%m-%d", &date)) + g_return_if_reached (); + append_field_and_value (self, _("Not Valid After"), display, FALSE); + } + g_free (display); + + /* Signature */ + append_heading (self, _("Signature")); + + /* TODO: Complete Signature algorithm, and params */ + append_field_and_value (self, _("Signature Algorithm"), "TODO", FALSE); + + value = egg_asn1_read_content (asn, data, n_data, "signature", &n_value); + g_return_if_fail (value); + display = egg_hex_encode_full (value, n_value, 1); + append_field_and_value (self, _("Signature"), display, TRUE); + g_free (display); + + /* Public Key Info */ + append_heading (self, _("Public Key Info")); + + /* TODO: Complete algorithm, params, key size */ + append_field_and_value (self, _("Key Algorithm"), "TODO", FALSE); + append_field_and_value (self, _("Key Size"), "TODO", FALSE); + + value = egg_asn1_read_content (asn, data, n_data, "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", &n_value); + g_return_if_fail (value); + display = egg_hex_encode_full (value, n_value, 1); + append_field_and_value (self, _("Public Key"), display, TRUE); + g_free (display); + + /* TODO: Implement extensions */ + + /* Fingerprints */ + append_heading (self, _("Fingerprints")); + + append_fingerprint (self, data, n_data, "SHA1", G_CHECKSUM_SHA1); + append_fingerprint (self, data, n_data, "MD5", G_CHECKSUM_MD5); + + asn1_delete_structure (&asn); +} + +/* ----------------------------------------------------------------------------- + * OBJECT + */ + +static GObject* +gcr_certificate_details_widget_constructor (GType type, guint n_props, GObjectConstructParam *props) +{ + GObject *obj = G_OBJECT_CLASS (gcr_certificate_details_widget_parent_class)->constructor (type, n_props, props); + GcrCertificateDetailsWidget *self = NULL; + GtkTextTagTable *tags; + GtkWidget *widget; + GtkWidget *scroll; + + g_return_val_if_fail (obj, NULL); + + self = GCR_CERTIFICATE_DETAILS_WIDGET (obj); + + tags = create_tag_table (self); + self->pv->buffer = gtk_text_buffer_new (tags); + g_object_unref (tags); + + widget = gtk_text_view_new_with_buffer (self->pv->buffer); + self->pv->view = GTK_TEXT_VIEW (widget); + gtk_text_view_set_editable (self->pv->view, FALSE); + + scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scroll), widget); + + gtk_container_add (GTK_CONTAINER (self), scroll); + gtk_widget_show_all (scroll); + + return obj; +} + +static void +gcr_certificate_details_widget_init (GcrCertificateDetailsWidget *self) +{ + self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidgetPrivate)); +} + +static void +gcr_certificate_details_widget_dispose (GObject *obj) +{ + GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj); + + if (self->pv->certificate) + g_object_unref (self->pv->certificate); + self->pv->certificate = NULL; + + G_OBJECT_CLASS (gcr_certificate_details_widget_parent_class)->dispose (obj); +} + +static void +gcr_certificate_details_widget_finalize (GObject *obj) +{ + GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj); + + g_assert (!self->pv->certificate); + + if (self->pv->buffer) + g_object_unref (self->pv->buffer); + self->pv->buffer = NULL; + + if (self->pv->field_tag) + g_object_unref (self->pv->field_tag); + self->pv->field_tag = NULL; + + G_OBJECT_CLASS (gcr_certificate_details_widget_parent_class)->finalize (obj); +} + +static void +gcr_certificate_details_widget_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj); + + switch (prop_id) { + case PROP_CERTIFICATE: + gcr_certificate_details_widget_set_certificate (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_details_widget_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj); + + switch (prop_id) { + case PROP_CERTIFICATE: + g_value_set_object (value, self->pv->certificate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_details_widget_class_init (GcrCertificateDetailsWidgetClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gcr_certificate_details_widget_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, sizeof (GcrCertificateDetailsWidgetPrivate)); + + gobject_class->constructor = gcr_certificate_details_widget_constructor; + gobject_class->dispose = gcr_certificate_details_widget_dispose; + gobject_class->finalize = gcr_certificate_details_widget_finalize; + gobject_class->set_property = gcr_certificate_details_widget_set_property; + gobject_class->get_property = gcr_certificate_details_widget_get_property; + + g_object_class_install_property (gobject_class, PROP_CERTIFICATE, + g_param_spec_object("certificate", "Certificate", "Certificate to display.", + GCR_TYPE_CERTIFICATE, G_PARAM_READWRITE)); +} + +/* ----------------------------------------------------------------------------- + * PUBLIC + */ + +GcrCertificateDetailsWidget* +gcr_certificate_details_widget_new (GcrCertificate *certificate) +{ + return g_object_new (GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, "certificate", certificate, NULL); +} + +GcrCertificate* +gcr_certificate_details_widget_get_certificate (GcrCertificateDetailsWidget *self) +{ + g_return_val_if_fail (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self), NULL); + return self->pv->certificate; +} + +void +gcr_certificate_details_widget_set_certificate (GcrCertificateDetailsWidget *self, GcrCertificate *cert) +{ + g_return_if_fail (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self)); + + if (self->pv->certificate) + g_object_unref (self->pv->certificate); + self->pv->certificate = cert; + if (self->pv->certificate) + g_object_ref (self->pv->certificate); + + refresh_display (self); + g_object_notify (G_OBJECT (self), "certificate"); +} diff --git a/gcr/gcr-certificate-details-widget.h b/gcr/gcr-certificate-details-widget.h new file mode 100644 index 00000000..74338c66 --- /dev/null +++ b/gcr/gcr-certificate-details-widget.h @@ -0,0 +1,57 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GCR_CERTIFICATE_DETAILS_WIDGET_H__ +#define __GCR_CERTIFICATE_DETAILS_WIDGET_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#include "gcr-certificate.h" + +#define GCR_TYPE_CERTIFICATE_DETAILS_WIDGET (gcr_certificate_details_widget_get_type ()) +#define GCR_CERTIFICATE_DETAILS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidget)) +#define GCR_CERTIFICATE_DETAILS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidgetClass)) +#define GCR_IS_CERTIFICATE_DETAILS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET)) +#define GCR_IS_CERTIFICATE_DETAILS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET)) +#define GCR_CERTIFICATE_DETAILS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidgetClass)) + +typedef struct _GcrCertificateDetailsWidget GcrCertificateDetailsWidget; +typedef struct _GcrCertificateDetailsWidgetClass GcrCertificateDetailsWidgetClass; +typedef struct _GcrCertificateDetailsWidgetPrivate GcrCertificateDetailsWidgetPrivate; + +struct _GcrCertificateDetailsWidget { + GtkAlignment parent; + GcrCertificateDetailsWidgetPrivate *pv; +}; + +struct _GcrCertificateDetailsWidgetClass { + GtkAlignmentClass parent_class; +}; + +GType gcr_certificate_details_widget_get_type (void); + +GcrCertificateDetailsWidget* gcr_certificate_details_widget_new (GcrCertificate *cert); + +GcrCertificate* gcr_certificate_details_widget_get_certificate (GcrCertificateDetailsWidget *details); + +void gcr_certificate_details_widget_set_certificate (GcrCertificateDetailsWidget *details, + GcrCertificate *cert); + +#endif /* __GCR_CERTIFICATE_DETAILS_WIDGET_H__ */ diff --git a/gcr/gcr-certificate.c b/gcr/gcr-certificate.c new file mode 100644 index 00000000..91eb1873 --- /dev/null +++ b/gcr/gcr-certificate.c @@ -0,0 +1,398 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "gcr-internal.h" +#include "gcr-certificate.h" + +#include "egg/egg-asn1.h" +#include "egg/egg-hex.h" + +#include <string.h> + +struct _GcrCertificatePrivate { + /* Cache of data returned from get_der_data() */ + ASN1_TYPE asn1; + gconstpointer data; + gsize n_data; + + /* When initialized with gcr_certificate_new_for_data() */ + guchar *owned_data; + gsize n_owned_data; +}; + +G_DEFINE_TYPE (GcrCertificate, gcr_certificate, G_TYPE_OBJECT); + +/* ----------------------------------------------------------------------------- + * INTERNAL + */ + +static ASN1_TYPE +parse_certificate_asn1 (GcrCertificate *self) +{ + const guchar *data; + gsize n_data; + + g_assert (GCR_IS_CERTIFICATE (self)); + + data = gcr_certificate_get_der_data (self, &n_data); + g_return_val_if_fail (data, NULL); + + if (self->pv->asn1 && n_data == self->pv->n_data && + memcmp (data, self->pv->data, n_data) == 0) + return self->pv->asn1; + + if (self->pv->asn1) { + asn1_delete_structure (&self->pv->asn1); + self->pv->asn1 = NULL; + self->pv->data = NULL; + self->pv->n_data = 0; + } + + /* Cache is invalid or non existent */ + self->pv->asn1 = egg_asn1_decode ("PKIX1.Certificate", data, n_data); + if (self->pv->asn1 == NULL) { + g_warning ("encountered invalid or unparseable X509 DER certificate data."); + return NULL; + } + + self->pv->data = data; + self->pv->n_data = n_data; + + return self->pv->asn1; +} + +static GChecksum* +digest_certificate (GcrCertificate *self, GChecksumType type) +{ + GChecksum *digest; + const guchar *der; + gsize n_der; + + g_assert (GCR_IS_CERTIFICATE (self)); + + der = gcr_certificate_get_der_data (self, &n_der); + g_return_val_if_fail (der, NULL); + + digest = g_checksum_new (type); + g_return_val_if_fail (digest, NULL); + + g_checksum_update (digest, der, n_der); + return digest; +} + +/* ----------------------------------------------------------------------------- + * OBJECT + */ + +static const guchar* +gcr_certificate_real_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); + g_return_val_if_fail (self->pv->owned_data, NULL); + + /* This is called when we're not a base class */ + *n_data = self->pv->n_owned_data; + return self->pv->owned_data; +} + +static GObject* +gcr_certificate_constructor (GType type, guint n_props, GObjectConstructParam *props) +{ + GcrCertificate *self = GCR_CERTIFICATE (G_OBJECT_CLASS (gcr_certificate_parent_class)->constructor(type, n_props, props)); + g_return_val_if_fail (self, NULL); + + return G_OBJECT (self); +} + +static void +gcr_certificate_init (GcrCertificate *self) +{ + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE, GcrCertificatePrivate); +} + +static void +gcr_certificate_dispose (GObject *obj) +{ + GcrCertificate *self = GCR_CERTIFICATE (obj); + + if (self->pv->asn1) { + asn1_delete_structure (&self->pv->asn1); + self->pv->data = NULL; + self->pv->n_data = 0; + } + + G_OBJECT_CLASS (gcr_certificate_parent_class)->dispose (obj); +} + +static void +gcr_certificate_finalize (GObject *obj) +{ + GcrCertificate *self = GCR_CERTIFICATE (obj); + + g_assert (self->pv->asn1 == NULL); + g_free (self->pv->owned_data); + self->pv->owned_data = NULL; + self->pv->n_owned_data = 0; + + G_OBJECT_CLASS (gcr_certificate_parent_class)->finalize (obj); +} + +static void +gcr_certificate_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_class_init (GcrCertificateClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructor = gcr_certificate_constructor; + gobject_class->dispose = gcr_certificate_dispose; + gobject_class->finalize = gcr_certificate_finalize; + gobject_class->set_property = gcr_certificate_set_property; + gobject_class->get_property = gcr_certificate_get_property; + + klass->get_der_data = gcr_certificate_real_get_der_data; + + g_type_class_add_private (gobject_class, sizeof (GcrCertificatePrivate)); + + _gcr_initialize (); +} + +/* ----------------------------------------------------------------------------- + * PUBLIC + */ + +GcrCertificate* +gcr_certificate_new_for_data (const guchar *data, gsize n_data) +{ + GcrCertificate *cert; + + g_return_val_if_fail (data, NULL); + g_return_val_if_fail (n_data, NULL); + + cert = g_object_new (GCR_TYPE_CERTIFICATE, NULL); + + cert->pv->owned_data = g_memdup (data, n_data); + cert->pv->n_owned_data = n_data; + return cert; +} + +const guchar* +gcr_certificate_get_der_data (GcrCertificate *self, gsize *n_length) +{ + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + g_return_val_if_fail (GCR_CERTIFICATE_GET_CLASS (self)->get_der_data, NULL); + return GCR_CERTIFICATE_GET_CLASS (self)->get_der_data (self, n_length); +} + +gchar* +gcr_certificate_get_issuer_cn (GcrCertificate *self) +{ + return gcr_certificate_get_issuer_part (self, "cn"); +} + +gchar* +gcr_certificate_get_issuer_part (GcrCertificate *self, const char *part) +{ + ASN1_TYPE asn1; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + return egg_asn1_read_dn_part (asn1, "tbsCertificate.issuer.rdnSequence", part); +} + +gchar* +gcr_certificate_get_issuer_dn (GcrCertificate *self) +{ + ASN1_TYPE asn1; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + return egg_asn1_read_dn (asn1, "tbsCertificate.issuer.rdnSequence"); +} + +gchar* +gcr_certificate_get_subject_cn (GcrCertificate *self) +{ + return gcr_certificate_get_subject_part (self, "cn"); +} + +gchar* +gcr_certificate_get_subject_part (GcrCertificate *self, const char *part) +{ + ASN1_TYPE asn1; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + return egg_asn1_read_dn_part (asn1, "tbsCertificate.subject.rdnSequence", part); +} + +gchar* +gcr_certificate_get_subject_dn (GcrCertificate *self) +{ + ASN1_TYPE asn1; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + return egg_asn1_read_dn (asn1, "tbsCertificate.issuer.rdnSequence"); +} + +GDate* +gcr_certificate_get_issued_date (GcrCertificate *self) +{ + ASN1_TYPE asn1; + GDate *date; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + date = g_date_new (); + if (!egg_asn1_read_date (asn1, "tbsCertificate.validity.notBefore", date)) { + g_date_free (date); + return NULL; + } + + return date; +} + +GDate* +gcr_certificate_get_expiry_date (GcrCertificate *self) +{ + ASN1_TYPE asn1; + GDate *date; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + date = g_date_new (); + if (!egg_asn1_read_date (asn1, "tbsCertificate.validity.notAfter", date)) { + g_date_free (date); + return NULL; + } + + return date; +} + +guchar* +gcr_certificate_get_fingerprint (GcrCertificate *self, GChecksumType type, gsize *n_digest) +{ + GChecksum *sum; + guchar *digest; + gssize length; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + g_return_val_if_fail (n_digest, NULL); + + sum = digest_certificate (self, type); + g_return_val_if_fail (sum, 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); + g_checksum_free (sum); + + return digest; +} + +gchar* +gcr_certificate_get_fingerprint_hex (GcrCertificate *self, GChecksumType type) +{ + GChecksum *sum; + gchar *hex; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + sum = digest_certificate (self, type); + g_return_val_if_fail (sum, NULL); + hex = g_strdup (g_checksum_get_string (sum)); + g_checksum_free (sum); + return hex; +} + +guchar* +gcr_certificate_get_serial_number (GcrCertificate *self, gsize *n_length) +{ + ASN1_TYPE asn1; + + g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL); + + asn1 = parse_certificate_asn1 (self); + g_return_val_if_fail (asn1, NULL); + + return egg_asn1_read_value (asn1, "tbsCertificate.serialNumber", n_length, g_realloc); +} + +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; +} diff --git a/gcr/gcr-certificate.h b/gcr/gcr-certificate.h new file mode 100644 index 00000000..c800f9d8 --- /dev/null +++ b/gcr/gcr-certificate.h @@ -0,0 +1,91 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GCR_CERTIFICATE_H__ +#define __GCR_CERTIFICATE_H__ + +#include "gcr.h" + +#include <glib-object.h> + +#define GCR_TYPE_CERTIFICATE (gcr_certificate_get_type ()) +#define GCR_CERTIFICATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE, GcrCertificate)) +#define GCR_CERTIFICATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE, GcrCertificateClass)) +#define GCR_IS_CERTIFICATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE)) +#define GCR_IS_CERTIFICATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE)) +#define GCR_CERTIFICATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE, GcrCertificateClass)) + +typedef struct _GcrCertificate GcrCertificate; +typedef struct _GcrCertificateClass GcrCertificateClass; +typedef struct _GcrCertificatePrivate GcrCertificatePrivate; + +struct _GcrCertificate { + GObject parent; + GcrCertificatePrivate *pv; +}; + +struct _GcrCertificateClass { + GObjectClass parent_class; + + /* virtual */ + + const guchar* (*get_der_data) (GcrCertificate *self, gsize *n_length); +}; + +GType gcr_certificate_get_type (void); + +GcrCertificate* gcr_certificate_new_for_data (const guchar *data, + gsize n_data); + +const guchar* gcr_certificate_get_der_data (GcrCertificate *self, + gsize *n_data); + +gchar* gcr_certificate_get_issuer_cn (GcrCertificate *self); + +gchar* gcr_certificate_get_issuer_dn (GcrCertificate *self); + +gchar* gcr_certificate_get_issuer_part (GcrCertificate *self, + const gchar *part); + +gchar* gcr_certificate_get_subject_cn (GcrCertificate *self); + +gchar* gcr_certificate_get_subject_dn (GcrCertificate *self); + +gchar* gcr_certificate_get_subject_part (GcrCertificate *self, + const gchar *part); + +GDate* gcr_certificate_get_issued_date (GcrCertificate *self); + +GDate* gcr_certificate_get_expiry_date (GcrCertificate *self); + +guchar* gcr_certificate_get_serial_number (GcrCertificate *self, + gsize *n_length); + +gchar* gcr_certificate_get_serial_number_hex (GcrCertificate *self); + +guchar* gcr_certificate_get_fingerprint (GcrCertificate *self, + GChecksumType type, + gsize *n_length); + +gchar* gcr_certificate_get_fingerprint_hex (GcrCertificate *self, + GChecksumType type); + +#endif /* __GCR_CERTIFICATE_H__ */ diff --git a/gcr/gcr-library.c b/gcr/gcr-library.c index 12d5ca59..55439897 100644 --- a/gcr/gcr-library.c +++ b/gcr/gcr-library.c @@ -180,7 +180,8 @@ _gcr_initialize (void) /* Only initialize libgcrypt if it hasn't already been initialized */ if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) { - gcry_control (GCRYCTL_SET_THREAD_CBS, &glib_thread_cbs); + if (g_thread_supported()) + gcry_control (GCRYCTL_SET_THREAD_CBS, &glib_thread_cbs); gcry_check_version (LIBGCRYPT_VERSION); gcry_set_log_handler (log_handler, NULL); gcry_set_outofcore_handler (no_mem_handler, NULL); diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c index d508ec97..f9c97f96 100644 --- a/gcr/gcr-parser.c +++ b/gcr/gcr-parser.c @@ -553,7 +553,7 @@ parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data) if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih)) break; - crypted = egg_asn1_read_value (asn, "encryptedData", &n_crypted, egg_secure_realloc); + crypted = egg_asn1_read_value (asn, "encryptedData", &n_crypted, (EggAllocator)egg_secure_realloc); if (!crypted) break; @@ -892,7 +892,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data) } crypted = egg_asn1_read_value (asn, "encryptedContentInfo.encryptedContent", - &n_crypted, egg_secure_realloc); + &n_crypted, (EggAllocator)egg_secure_realloc); if (!crypted) goto done; diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am index 4a055852..6bc48b35 100644 --- a/gcr/tests/Makefile.am +++ b/gcr/tests/Makefile.am @@ -1,10 +1,9 @@ # Test files should be listed in order they need to run UNIT_AUTO = \ + unit-test-certificate.c \ unit-test-parser.c -UNIT_PROMPT = - UNIT_LIBS = \ $(top_builddir)/gcr/libgcr.la \ $(top_builddir)/egg/libegg.la \ @@ -15,3 +14,22 @@ UNIT_FLAGS = \ -DGCR_API_SUBJECT_TO_CHANGE include $(top_srcdir)/tests/gtest.make + +# ------------------------------------------------------------------ + +noinst_PROGRAMS += \ + ui-test-details + +ui_test_details_SOURCES = \ + ui-test-details.c + +ui_test_details_CFLAGS = \ + -DGCR_API_SUBJECT_TO_CHANGE \ + $(GTK_CFLAGS) + +ui_test_details_LDADD = \ + $(top_builddir)/gcr/libgcr.la \ + $(GTK_LIBS) \ + $(LIBGCRYPT_LIBS) \ + $(LIBTASN1_LIBS) +
\ No newline at end of file diff --git a/gcr/tests/ui-test-details.c b/gcr/tests/ui-test-details.c new file mode 100644 index 00000000..828b1075 --- /dev/null +++ b/gcr/tests/ui-test-details.c @@ -0,0 +1,45 @@ + +#include "config.h" + +#include "gcr-certificate-details-widget.h" + +#include <gtk/gtk.h> + +static void +test_details (void) +{ + GcrCertificateDetailsWidget *details; + GcrCertificate *certificate; + GtkDialog *dialog; + guchar *data; + gsize n_data; + + if (!g_file_get_contents ("test-data/der-certificate.crt", (gchar**)&data, &n_data, NULL)) + g_assert_not_reached (); + + certificate = gcr_certificate_new_for_data (data, n_data); + g_assert (certificate); + g_free (data); + + dialog = GTK_DIALOG (gtk_dialog_new ()); + g_object_ref_sink (dialog); + + details = gcr_certificate_details_widget_new (certificate); + gtk_widget_show (GTK_WIDGET (details)); + gtk_container_add (GTK_CONTAINER (dialog->vbox), GTK_WIDGET (details)); + + gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 400); + gtk_dialog_run (dialog); + + g_object_unref (dialog); + g_object_unref (certificate); + g_object_unref (details); +} + +int +main(int argc, char *argv[]) +{ + gtk_init (&argc, &argv); + test_details (); + return 0; +} diff --git a/gcr/tests/unit-test-certificate.c b/gcr/tests/unit-test-certificate.c new file mode 100644 index 00000000..452dacbf --- /dev/null +++ b/gcr/tests/unit-test-certificate.c @@ -0,0 +1,139 @@ + +#include "config.h" +#include "run-auto-test.h" + +#include "gcr-certificate.h" + +#include <glib.h> + +#include <string.h> + +static GcrCertificate *certificate = NULL; + +DEFINE_SETUP(certificate) +{ + GError *err = NULL; + gchar *contents; + gsize n_contents; + + if (!g_file_get_contents ("test-data/der-certificate.crt", &contents, &n_contents, &err)) { + g_warning ("couldn't read test-data/test-certificate-1.der: %s", err->message); + return; + } + + certificate = gcr_certificate_new_for_data ((const guchar*)contents, n_contents); + g_assert (certificate); + g_free (contents); +} + +DEFINE_TEARDOWN(certificate) +{ + if (certificate) + g_object_unref (certificate); + certificate = NULL; +} + +DEFINE_TEST(issuer_cn) +{ + gchar *cn = gcr_certificate_get_issuer_cn (certificate); + g_assert (cn); + g_assert_cmpstr (cn, ==, "http://www.valicert.com/"); + g_free (cn); +} + +DEFINE_TEST(issuer_dn) +{ + gchar *dn = gcr_certificate_get_issuer_dn (certificate); + g_assert (dn); + g_assert_cmpstr (dn, ==, "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 3 Policy Validation Authority, CN=http://www.valicert.com/, EMAIL=info@valicert.com"); + g_free (dn); +} + +DEFINE_TEST(issuer_part) +{ + gchar *part = gcr_certificate_get_issuer_part (certificate, "l"); + g_assert (part); + g_assert_cmpstr (part, ==, "ValiCert Validation Network"); + g_free (part); +} + +DEFINE_TEST(subject_cn) +{ + gchar *cn = gcr_certificate_get_subject_cn (certificate); + g_assert (cn); + g_assert_cmpstr (cn, ==, "http://www.valicert.com/"); + g_free (cn); +} + +DEFINE_TEST(subject_dn) +{ + gchar *dn = gcr_certificate_get_subject_dn (certificate); + g_assert (dn); + g_assert_cmpstr (dn, ==, "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 3 Policy Validation Authority, CN=http://www.valicert.com/, EMAIL=info@valicert.com"); + g_free (dn); +} + +DEFINE_TEST(subject_part) +{ + gchar *part = gcr_certificate_get_subject_part (certificate, "OU"); + g_assert (part); + g_assert_cmpstr (part, ==, "ValiCert Class 3 Policy Validation Authority"); + g_free (part); +} + +DEFINE_TEST(issued_date) +{ + GDate *date = gcr_certificate_get_issued_date (certificate); + g_assert (date); + g_assert_cmpuint (g_date_get_year (date), ==, 1999); + g_assert_cmpuint (g_date_get_month (date), ==, 6); + g_assert_cmpuint (g_date_get_day (date), ==, 26); + g_date_free (date); +} + +DEFINE_TEST(expiry_date) +{ + GDate *date = gcr_certificate_get_expiry_date (certificate); + g_assert (date); + g_assert_cmpuint (g_date_get_year (date), ==, 2019); + g_assert_cmpuint (g_date_get_month (date), ==, 6); + g_assert_cmpuint (g_date_get_day (date), ==, 26); + g_date_free (date); +} + +DEFINE_TEST(serial_number) +{ + gsize n_serial; + guchar *serial; + gchar *hex; + + serial = gcr_certificate_get_serial_number (certificate, &n_serial); + g_assert (serial); + g_assert_cmpuint (n_serial, ==, 1); + g_assert (memcmp (serial, "\1", n_serial) == 0); + g_free (serial); + + hex = gcr_certificate_get_serial_number_hex (certificate); + g_assert (hex); + g_assert_cmpstr (hex, ==, "01"); + g_free (hex); +} + +DEFINE_TEST(fingerprint) +{ + gsize n_print; + guchar *print = gcr_certificate_get_fingerprint (certificate, G_CHECKSUM_MD5, &n_print); + g_assert (print); + g_assert_cmpuint (n_print, ==, g_checksum_type_get_length (G_CHECKSUM_MD5)); + g_assert (memcmp (print, "\xa2\x6f\x53\xb7\xee\x40\xdb\x4a\x68\xe7\xfa\x18\xd9\x10\x4b\x72", n_print) == 0); + g_free (print); +} + +DEFINE_TEST(fingerprint_hex) +{ + gchar *print = gcr_certificate_get_fingerprint_hex (certificate, G_CHECKSUM_MD5); + g_assert (print); + g_assert_cmpstr (print, ==, "a26f53b7ee40db4a68e7fa18d9104b72"); + g_free (print); +} + diff --git a/po/ChangeLog b/po/ChangeLog index a491ec6e..26caef32 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,10 @@ +2009-01-27 Stef Walter <stef@memberwebs.com> + + * POTFILES.in: Added egg/egg-asn1.c + gcr/gcr-certificate-basics-widget.c + gcr/gcr-certificate-basics-widget.glade + gcr/gcr-certificate-details-widget.c + 2009-01-27 Christian Kirbach <Christian.Kirbach@googlemail.com> * de.po: Updated German translation. diff --git a/po/POTFILES.in b/po/POTFILES.in index bad908b3..fdf2b0b8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,6 +15,10 @@ daemon/pkcs11/gkr-pkcs11-auth.c daemon/pkix/gkr-pkix-parser.c daemon/pkix/gkr-pkix-serialize.c daemon/ui/gkr-ask-tool.c +egg/egg-asn1.c +gcr/gcr-certificate-basics-widget.c +gcr/gcr-certificate-basics-widget.glade +gcr/gcr-certificate-details-widget.c gcr/gcr-import-dialog.glade gcr/gcr-importer.c gcr/gcr-parser.c @@ -22,4 +26,3 @@ gp11/gp11-misc.c library/gnome-keyring-utils.c pkcs11/gck/gck-certificate.c pkcs11/ssh-store/gck-ssh-private-key.c - diff --git a/po/POTFILES.skip b/po/POTFILES.skip index f7d718cd..fcfbed80 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -1,2 +1,3 @@ daemon/gnome-keyring-daemon.desktop.in gcr/gcr-import-dialog.ui +gcr/gcr-certificate-basics-widget.ui |