From: Stefan Walter Date: Sat, 17 Jan 2009 23:27:10 +0000 (+0000) Subject: Consolidate truly common functionality into 'egg' library. Many more files X-Git-Tag: split~377 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a52d75ec21ac6fec61e2ea2216308f6742c2c975;p=platform%2Fupstream%2Fgcr.git Consolidate truly common functionality into 'egg' library. Many more files * egg/egg-asn1.c: (moved from pkcs11/gck/gck-data-asn1.c) * egg/egg-asn1.h: (moved from pkcs11/gck/gck-data-asn1.h) * egg/egg-buffer.c: (moved from common/gkr-buffer.c) * egg/egg-buffer.h: (moved from common/gkr-buffer.h) * egg/egg-secure-memory.c: (moved from common/gkr-secure-memory.c) * egg/egg-secure-memory.h: (moved from common/gkr-secure-memory.h) * egg/egg-unix-credentials.c: (moved from common/gkr-unix-credentials.c) * egg/egg-unix-credentials.h: (moved from common/gkr-unix-credentials.h) * egg/Makefile.am: (added) * egg/pk.asn: (moved from pkcs11/gck/pk.asn) * egg/pkix.asn: (moved from pkcs11/gck/pkix.asn) * egg/tests/Makefile.am: (added) * egg/tests/test.asn: (moved from pkcs11/gck/tests/test.asn) * egg/tests/unit-test-asn1.c: (moved from pkcs11/gck/tests/unit-test-data-asn1.c) * egg/tests/unit-test-secmem.c: (moved from common/tests/unit-test-secmem.c) * egg/tests/test-data: (added) Consolidate truly common functionality into 'egg' library. Many more files touched due to above changes. svn path=/trunk/; revision=1461 --- diff --git a/egg/Makefile.am b/egg/Makefile.am new file mode 100644 index 0000000..a548c96 --- /dev/null +++ b/egg/Makefile.am @@ -0,0 +1,55 @@ + +noinst_LTLIBRARIES = \ + libegg.la \ + libegg-buffer.la \ + libegg-creds.la \ + libegg-secure.la + +BUILT_SOURCES = \ + asn1-def-pk.h asn1-def-pkix.h + +INCLUDES = \ + -I$(top_srcdir) + -I$(top_builddir) + +libegg_la_CFLAGS = \ + $(LIBTASN1_CFLAGS) \ + $(GLIB_CFLAGS) + +libegg_la_SOURCES = \ + egg-asn1.c egg-asn1.h \ + egg-buffer.c egg-buffer.h \ + egg-unix-credentials.c egg-unix-credentials.h \ + egg-secure-memory.c egg-secure-memory.h + +asn1-def-pk.h: pk.asn + asn1Parser -o asn1-def-pk.h pk.asn + +asn1-def-pkix.h: pkix.asn + asn1Parser -o asn1-def-pkix.h pkix.asn + +EXTRA_DIST = \ + pkix.asn \ + pk.asn + +# -------------------------------------------------------------------- +# COMMON STUFF COMPILED INTO SMALLER COMPONENTS + +libegg_secure_la_SOURCES = \ + egg-secure-memory.c egg-secure-memory.h + +libegg_buffer_la_SOURCES = \ + egg-buffer.c egg-buffer.h + +libegg_creds_la_SOURCES = \ + egg-unix-credentials.c egg-unix-credentials.h + +# ------------------------------------------------------------------- + +if WITH_TESTS +TESTS_DIR = tests +else +TESTS_DIR = +endif + +SUBDIRS = . $(TESTS_DIR) diff --git a/egg/egg-asn1.c b/egg/egg-asn1.c new file mode 100644 index 0000000..a127d50 --- /dev/null +++ b/egg/egg-asn1.c @@ -0,0 +1,1015 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* egg-asn1.c - ASN.1 helper routines + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#include "config.h" + +#include "egg-asn1.h" + +#include + +#include + +/* + * HACK: asn1Parser defines these arrays as extern const, which gives + * gcc a fit. So we def it out. + */ + +#define extern +#include "asn1-def-pk.h" +#include "asn1-def-pkix.h" +#undef extern + +static ASN1_TYPE asn1_pk = NULL; +static ASN1_TYPE asn1_pkix = NULL; + +static void +init_asn1_trees (void) +{ + static volatile gsize asn1_initialized = 0; + int res; + + if (g_once_init_enter (&asn1_initialized)) { + res = asn1_array2tree (pk_asn1_tab, &asn1_pk, NULL); + g_return_if_fail (res == ASN1_SUCCESS); + res = asn1_array2tree (pkix_asn1_tab, &asn1_pkix, NULL); + g_return_if_fail (res == ASN1_SUCCESS); + g_once_init_leave (&asn1_initialized, 1); + } +} + +ASN1_TYPE +egg_asn1_get_pk_asn1type (void) +{ + init_asn1_trees (); + return asn1_pk; +} + +ASN1_TYPE +egg_asn1_get_pkix_asn1type (void) +{ + init_asn1_trees (); + return asn1_pkix; +} + +ASN1_TYPE +egg_asn1_decode (const gchar *type, const guchar *data, gsize n_data) +{ + ASN1_TYPE base = ASN1_TYPE_EMPTY; + ASN1_TYPE asn; + int res; + + if (strncmp (type, "PKIX1.", 6) == 0) + base = egg_asn1_get_pkix_asn1type (); + else if (strncmp (type, "PK.", 3) == 0) + base = egg_asn1_get_pk_asn1type (); + else + g_return_val_if_reached (NULL); + + res = asn1_create_element (base, type, &asn); + g_return_val_if_fail (res == ASN1_SUCCESS, NULL); + + res = asn1_der_decoding (&asn, data, n_data, NULL); + if (res != ASN1_SUCCESS) { + asn1_delete_structure (&asn); + return NULL; + } + + return asn; +} + +guchar* +egg_asn1_encode (ASN1_TYPE asn, const gchar* part, gsize *n_data, EggAllocator alloc) +{ + guchar *data; + int res, len; + + g_assert (asn); + g_assert (n_data); + + len = 0; + res = asn1_der_coding (asn, part, NULL, &len, NULL); + g_return_val_if_fail (res == ASN1_MEM_ERROR, NULL); + + if (!alloc) + alloc = (EggAllocator)g_realloc; + + data = (alloc) (NULL, len); + g_return_val_if_fail (data != NULL, NULL); + + res = asn1_der_coding (asn, part, data, &len, NULL); + if (res != ASN1_SUCCESS) { + (alloc) (data, 0); + return NULL; + } + + *n_data = len; + return data; +} + +gint +egg_asn1_element_length (const guchar *data, gsize n_data) +{ + guchar cls; + int counter = 0; + int cb, len; + gulong tag; + + if (asn1_get_tag_der (data, n_data, &cls, &cb, &tag) == ASN1_SUCCESS) { + counter += cb; + len = asn1_get_length_der (data + cb, n_data - cb, &cb); + counter += cb; + if (len >= 0) { + len += counter; + if (n_data >= len) + return len; + } + } + + return -1; +} + +const guchar* +egg_asn1_read_element (ASN1_TYPE asn, const guchar *data, gsize n_data, + const gchar *part, gsize *n_element) +{ + int beg, end, res; + + g_return_val_if_fail (asn != NULL, NULL); + g_return_val_if_fail (part != NULL, NULL); + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (n_element != NULL, NULL); + + res = asn1_der_decoding_startEnd (asn, data, n_data, part, &beg, &end); + if (res != ASN1_SUCCESS) + return NULL; + + *n_element = end - beg + 1; + return data + beg; +} + +const guchar* +egg_asn1_read_content (ASN1_TYPE asn, const guchar *data, gsize n_data, + const gchar *part, gsize *n_content) +{ + const guchar *raw; + gsize n_raw; + + g_return_val_if_fail (asn != NULL, NULL); + g_return_val_if_fail (part != NULL, NULL); + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (n_content != NULL, NULL); + + raw = egg_asn1_read_element (asn, data, n_data, part, &n_raw); + if (!raw) + return NULL; + + return egg_asn1_element_content (raw, n_raw, n_content); +} + +const guchar* +egg_asn1_element_content (const guchar *data, gsize n_data, gsize *n_content) +{ + int counter = 0; + guchar cls; + gulong tag; + int cb, len; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (n_content != NULL, NULL); + + /* Now get the data out of this element */ + if (asn1_get_tag_der (data, n_data, &cls, &cb, &tag) != ASN1_SUCCESS) + return NULL; + + counter += cb; + len = asn1_get_length_der (data + cb, n_data - cb, &cb); + if (len < 0) + return NULL; + counter += cb; + + *n_content = len; + return data + counter; +} + +guchar* +egg_asn1_read_value (ASN1_TYPE asn, const gchar *part, gsize *len, EggAllocator allocator) +{ + int l, res; + guchar *buf; + + g_return_val_if_fail (asn != NULL, NULL); + g_return_val_if_fail (part != NULL, NULL); + + if (allocator == NULL) + allocator = (EggAllocator)g_realloc; + + l = 0; + res = asn1_read_value (asn, part, NULL, &l); + g_return_val_if_fail (res != ASN1_SUCCESS, NULL); + if (res != ASN1_MEM_ERROR) + return NULL; + + /* Always null terminate it, just for convenience */ + buf = (allocator) (NULL, l + 1); + g_return_val_if_fail (buf, NULL); + memset (buf, 0, l + 1); + + res = asn1_read_value (asn, part, buf, &l); + if (res != ASN1_SUCCESS) { + (allocator) (buf, 0); + buf = NULL; + } else if (len) { + *len = l; + } + + return buf; +} + +gboolean +egg_asn1_write_value (ASN1_TYPE asn, const gchar *part, + const guchar* value, gsize len) +{ + int res; + + g_return_val_if_fail (asn, FALSE); + g_return_val_if_fail (part, FALSE); + g_return_val_if_fail (!len || value, FALSE); + + res = asn1_write_value (asn, part, (const void*)value, (int)len); + return res == ASN1_SUCCESS; +} + +gboolean +egg_asn1_read_boolean (ASN1_TYPE asn, const gchar *part, gboolean *val) +{ + gchar buffer[32]; + int n_buffer = sizeof (buffer) - 1; + int res; + + memset (buffer, 0, sizeof (buffer)); + + res = asn1_read_value (asn, part, buffer, &n_buffer); + if (res != ASN1_SUCCESS) + return FALSE; + + if (g_ascii_strcasecmp (buffer, "TRUE") == 0) + *val = TRUE; + else + *val = FALSE; + + return TRUE; +} + +gboolean +egg_asn1_read_uint (ASN1_TYPE asn, const gchar *part, guint *val) +{ + guchar buf[4]; + int n_buf = sizeof (buf); + gsize i; + int res; + + res = asn1_read_value (asn, part, buf, &n_buf); + if(res != ASN1_SUCCESS) + return FALSE; + + if (n_buf > 4 || n_buf < 1) + return FALSE; + + *val = 0; + for (i = 0; i < n_buf; ++i) + *val |= buf[i] << (8 * ((n_buf - 1) - i)); + + return TRUE; +} + +gboolean +egg_asn1_write_uint (ASN1_TYPE asn, const gchar *part, guint32 val) +{ + guchar buf[4]; + int res, bytes; + + buf[0] = (val >> 24) & 0xff; + buf[1] = (val >> 16) & 0xff; + buf[2] = (val >> 8) & 0xff; + buf[3] = (val >> 0) & 0xff; + + for (bytes = 3; bytes >= 0; --bytes) + if (!buf[bytes]) + break; + + bytes = 4 - (bytes + 1); + if (bytes == 0) + bytes = 1; + res = asn1_write_value (asn, part, buf + (4 - bytes), bytes); + return res == ASN1_SUCCESS; +} + +GQuark +egg_asn1_read_oid (ASN1_TYPE asn, const gchar *part) +{ + GQuark quark; + guchar *buf; + gsize n_buf; + + buf = egg_asn1_read_value (asn, part, &n_buf, NULL); + if (!buf) + return 0; + + quark = g_quark_try_string ((gchar*)buf); + g_free (buf); + + if (quark == 0) + quark = g_quark_from_static_string ("0.UNKNOWN.OID"); + + return quark; +} + +gboolean +egg_asn1_write_oid (ASN1_TYPE asn, const gchar *part, GQuark val) +{ + const gchar* oid; + + g_return_val_if_fail (val, FALSE); + + oid = g_quark_to_string (val); + g_return_val_if_fail (oid, FALSE); + + return egg_asn1_write_value (asn, part, (const guchar*)oid, strlen (oid)); +} + +static int +atoin (const char *p, int digits) +{ + int ret = 0, base = 1; + while(--digits >= 0) { + if (p[digits] < '0' || p[digits] > '9') + return -1; + ret += (p[digits] - '0') * base; + base *= 10; + } + return ret; +} + +static int +two_to_four_digit_year (int year) +{ + time_t now; + struct tm tm; + int century, current; + + g_return_val_if_fail (year >= 0 && year <= 99, -1); + + /* Get the current year */ + now = time (NULL); + g_return_val_if_fail (now >= 0, -1); + if (!gmtime_r (&now, &tm)) + g_return_val_if_reached (-1); + + current = (tm.tm_year % 100); + century = (tm.tm_year + 1900) - current; + + /* + * Check if it's within 40 years before the + * current date. + */ + if (current < 40) { + if (year < current) + return century + year; + if (year > 100 - (40 - current)) + return (century - 100) + year; + } else { + if (year < current && year > (current - 40)) + return century + year; + } + + /* + * If it's after then adjust for overflows to + * the next century. + */ + if (year < current) + return century + 100 + year; + else + return century + year; +} + +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *t) +{ + time_t tl, tb; + struct tm *tg; + + tl = mktime (t); + if (tl == -1) + { + t->tm_hour--; + tl = mktime (t); + if (tl == -1) + return -1; /* can't deal with output from strptime */ + tl += 3600; + } + tg = gmtime (&tl); + tg->tm_isdst = 0; + tb = mktime (tg); + if (tb == -1) + { + tg->tm_hour--; + tb = mktime (tg); + if (tb == -1) + return -1; /* can't deal with output from gmtime */ + tb += 3600; + } + return (tl - (tb - tl)); +} +#endif //NOT_HAVE_TIMEGM + +time_t +egg_asn1_parse_utc_time (const gchar *time) +{ + struct tm when; + guint n_time; + time_t result; + const char *p, *e; + int year; + + g_assert (time); + n_time = strlen (time); + + /* YYMMDDhhmmss.ffff Z | +0000 */ + if (n_time < 6 || n_time >= 28) + return -1; + + /* Reset everything to default legal values */ + memset (&when, 0, sizeof (when)); + when.tm_mday = 1; + + /* Select the digits part of it */ + p = time; + for (e = p; *e >= '0' && *e <= '9'; ++e); + + if (p + 2 <= e) { + year = atoin (p, 2); + p += 2; + + /* + * 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; + } + if (p + 2 <= e) { + when.tm_mon = atoin (p, 2) - 1; + p += 2; + } + if (p + 2 <= e) { + when.tm_mday = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when.tm_hour = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when.tm_min = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + 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; + + /* Make sure all that got parsed */ + if (p != e) + return -1; + + /* 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; + + /* See if there's a fraction, and discard it if so */ + if (p < e && *p == '.' && p + 5 <= e) + p += 5; + + /* See if it's UTC */ + if (p < e && *p == 'Z') { + p += 1; + + /* See if it has a timezone */ + } else if ((*p == '-' || *p == '+') && p + 3 <= e) { + int off, neg; + + neg = *p == '-'; + ++p; + + off = atoin (p, 2) * 3600; + if (off < 0 || off > 86400) + return -1; + p += 2; + + if (p + 2 <= e) { + off += atoin (p, 2) * 60; + p += 2; + } + + /* Use TZ offset */ + if (neg) + result -= off; + else + result += off; + } + + /* Make sure everything got parsed */ + if (p != e) + return -1; + + return result; +} + +time_t +egg_asn1_parse_general_time (const gchar *time) +{ + struct tm when; + guint n_time; + time_t result; + const char *p, *e; + + g_assert (time); + n_time = strlen (time); + + /* YYYYMMDDhhmmss.ffff Z | +0000 */ + if (n_time < 8 || n_time >= 30) + return -1; + + /* Reset everything to default legal values */ + memset (&when, 0, sizeof (when)); + 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; + p += 4; + } + if (p + 2 <= e) { + when.tm_mon = atoin (p, 2) - 1; + p += 2; + } + if (p + 2 <= e) { + when.tm_mday = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when.tm_hour = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when.tm_min = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + 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; + + /* Make sure all that got parsed */ + if (p != e) + return -1; + + /* Covnvert to seconds since epoch */ + result = timegm (&when); + + /* Now the remaining optional stuff */ + e = time + n_time; + + /* See if there's a fraction, and discard it if so */ + if (p < e && *p == '.' && p + 5 <= e) + p += 5; + + /* See if it's UTC */ + if (p < e && *p == 'Z') { + p += 1; + + /* See if it has a timezone */ + } else if ((*p == '-' || *p == '+') && p + 3 <= e) { + int off, neg; + + neg = *p == '-'; + ++p; + + off = atoin (p, 2) * 3600; + if (off < 0 || off > 86400) + return -1; + p += 2; + + if (p + 2 <= e) { + off += atoin (p, 2) * 60; + p += 2; + } + + /* Use TZ offset */ + if (neg) + result -= off; + else + result += off; + } + + /* Make sure everything got parsed */ + if (p != e) + return -1; + + return result; +} + +gboolean +egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val) +{ + #define MAX_TIME 1024 + gchar ttime[MAX_TIME]; + gchar *name; + int len, res; + + len = sizeof (ttime) - 1; + res = asn1_read_value (asn, part, ttime, &len); + if (res != ASN1_SUCCESS) + return FALSE; + + /* CHOICE */ + if (strcmp (ttime, "generalTime") == 0) { + name = g_strconcat (part, ".generalTime", NULL); + len = sizeof (ttime) - 1; + res = asn1_read_value (asn, name, ttime, &len); + g_free (name); + if (res != ASN1_SUCCESS) + return FALSE; + + *val = egg_asn1_parse_general_time (ttime); + + /* UTCTIME */ + } else { + name = g_strconcat (part, ".utcTime", NULL); + len = sizeof (ttime) - 1; + res = asn1_read_value (asn, name, ttime, &len); + g_free (name); + if (res != ASN1_SUCCESS) + return FALSE; + + *val = egg_asn1_parse_utc_time (ttime); + } + + if (*val < (time_t)0) + return FALSE; + + return TRUE; +} + + +/* ------------------------------------------------------------------------------- + * Reading DN's + */ + +typedef struct _PrintableOid { + GQuark oid; + const gchar *oidstr; + const gchar *display; + 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 } +}; + +static void +init_printable_oids (void) +{ + static volatile gsize inited_oids = 0; + int i; + + if (g_once_init_enter (&inited_oids)) { + for (i = 0; printable_oids[i].oidstr != NULL; ++i) + printable_oids[i].oid = g_quark_from_static_string (printable_oids[i].oidstr); + g_once_init_leave (&inited_oids, 1); + } +} + +static PrintableOid* +dn_find_printable (GQuark oid) +{ + int i; + + g_return_val_if_fail (oid != 0, NULL); + + for (i = 0; printable_oids[i].oidstr != NULL; ++i) { + if (printable_oids[i].oid == oid) + return &printable_oids[i]; + } + + return NULL; +} + +static const char HEXC[] = "0123456789ABCDEF"; + +static gchar* +dn_print_hex_value (const guchar *data, gsize len) +{ + GString *result = g_string_sized_new (len * 2 + 1); + gsize i; + + g_string_append_c (result, '#'); + for (i = 0; i < len; ++i) { + g_string_append_c (result, HEXC[data[i] >> 4 & 0xf]); + g_string_append_c (result, HEXC[data[i] & 0xf]); + } + + return g_string_free (result, FALSE); +} + +static gchar* +dn_print_oid_value_parsed (PrintableOid *printable, guchar *data, gsize len) +{ + const gchar *asn_name; + ASN1_TYPE asn1; + gchar *part; + gchar *value; + + g_assert (printable); + g_assert (data); + g_assert (len); + g_assert (printable->oid); + + asn_name = asn1_find_structure_from_oid (egg_asn1_get_pkix_asn1type (), + printable->oidstr); + g_return_val_if_fail (asn_name, NULL); + + part = g_strdup_printf ("PKIX1.%s", asn_name); + asn1 = egg_asn1_decode (part, data, len); + g_free (part); + + if (!asn1) { + g_message ("couldn't decode value for OID: %s", printable->oidstr); + return NULL; + } + + value = (gchar*)egg_asn1_read_value (asn1, "", NULL, NULL); + + /* + * If it's a choice element, then we have to read depending + * on what's there. + */ + if (value && printable->is_choice) { + if (strcmp ("printableString", value) == 0 || + strcmp ("ia5String", value) == 0 || + strcmp ("utf8String", value) == 0 || + strcmp ("teletexString", value) == 0) { + part = value; + value = (gchar*)egg_asn1_read_value (asn1, part, NULL, NULL); + g_free (part); + } else { + g_free (value); + return NULL; + } + } + + if (!value) { + g_message ("couldn't read value for OID: %s", printable->oidstr); + return NULL; + } + + /* + * Now we make sure it's UTF-8. + */ + if (!g_utf8_validate (value, -1, NULL)) { + gchar *hex = dn_print_hex_value ((guchar*)value, strlen (value)); + g_free (value); + value = hex; + } + + return value; +} + +static gchar* +dn_print_oid_value (PrintableOid *printable, guchar *data, gsize len) +{ + gchar *value; + + g_assert (data); + g_assert (len); + + if (printable) { + value = dn_print_oid_value_parsed (printable, data, len); + if (value != NULL) + return value; + } + + return dn_print_hex_value (data, len); +} + +static gchar* +dn_parse_rdn (ASN1_TYPE asn, const gchar *part) +{ + PrintableOid *printable; + GQuark oid; + gchar *path; + guchar *value; + gsize n_value; + gchar *display; + gchar *result; + + g_assert (asn); + g_assert (part); + + path = g_strdup_printf ("%s.type", part); + oid = egg_asn1_read_oid (asn, path); + g_free (path); + + if (!oid) + return NULL; + + path = g_strdup_printf ("%s.value", part); + value = egg_asn1_read_value (asn, path, &n_value, NULL); + g_free (path); + + printable = dn_find_printable (oid); + + 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), + "=", display, NULL); + g_free (display); + + return result; +} + +gchar* +egg_asn1_read_dn (ASN1_TYPE asn, const gchar *part) +{ + gboolean done = FALSE; + GString *result; + gchar *path; + gchar *rdn; + gint i, j; + + g_return_val_if_fail (asn, NULL); + g_return_val_if_fail (part, NULL); + + init_printable_oids (); + + result = g_string_sized_new (64); + + /* Each (possibly multi valued) RDN */ + for (i = 1; !done; ++i) { + + /* Each type=value pair of an RDN */ + for (j = 1; TRUE; ++j) { + path = g_strdup_printf ("%s%s?%u.?%u", part ? part : "", + part ? "." : "", i, j); + rdn = dn_parse_rdn (asn, path); + g_free (path); + + if (!rdn) { + done = j == 1; + break; + } + + /* Account for multi valued RDNs */ + if (j > 1) + g_string_append (result, "+"); + else if (i > 1) + g_string_append (result, ", "); + + g_string_append (result, rdn); + g_free (rdn); + } + } + + /* Returns null when string is empty */ + return g_string_free (result, (result->len == 0)); +} + +gchar* +egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match) +{ + PrintableOid *printable = NULL; + gboolean done = FALSE; + guchar *value; + gsize n_value; + gchar *path; + GQuark oid; + gint i, j; + + g_return_val_if_fail (asn, NULL); + g_return_val_if_fail (part, NULL); + g_return_val_if_fail (match, NULL); + + 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) { + 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; + } + + /* 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) + continue; + } + + 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); + + g_return_val_if_fail (value, NULL); + return dn_print_oid_value (printable, value, n_value); + } + } + + return NULL; +} diff --git a/egg/egg-asn1.h b/egg/egg-asn1.h new file mode 100644 index 0000000..4e74f9d --- /dev/null +++ b/egg/egg-asn1.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* egg-asn1.h - ASN.1 helper routines + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#ifndef EGG_ASN1_H_ +#define EGG_ASN1_H_ + +#include + +#include + +typedef void* (*EggAllocator) (void* p, unsigned long len); + +ASN1_TYPE egg_asn1_get_pk_asn1type (void); + +ASN1_TYPE egg_asn1_get_pkix_asn1type (void); + +ASN1_TYPE egg_asn1_decode (const gchar *type, const guchar *data, + gsize n_data); + +guchar* egg_asn1_encode (ASN1_TYPE asn, const gchar* part, + gsize *len, EggAllocator alloc); + +guchar* egg_asn1_read_value (ASN1_TYPE asn, const gchar *part, + gsize *len, EggAllocator alloc); + +gboolean egg_asn1_write_value (ASN1_TYPE asn, const gchar *part, + const guchar* value, gsize len); + +GQuark egg_asn1_read_oid (ASN1_TYPE asn, const gchar *part); + +gboolean egg_asn1_write_oid (ASN1_TYPE asn, const gchar *part, GQuark val); + +gboolean egg_asn1_read_boolean (ASN1_TYPE asn, const gchar *part, gboolean *val); + +gboolean egg_asn1_read_uint (ASN1_TYPE asn, const gchar *part, guint *val); + +gboolean egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val); + +const guchar* egg_asn1_read_content (ASN1_TYPE asn, const guchar *data, gsize n_data, + const gchar *part, gsize *n_content); + +const guchar* egg_asn1_read_element (ASN1_TYPE asn, const guchar *data, gsize n_data, + const gchar *part, gsize *n_element); + +gboolean egg_asn1_write_uint (ASN1_TYPE asn, const gchar *part, guint val); + +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_parse_utc_time (const gchar* value); + +glong egg_asn1_parse_general_time (const gchar* value); + +#endif /*EGG_ASN1_H_*/ diff --git a/egg/egg-buffer.c b/egg/egg-buffer.c new file mode 100644 index 0000000..bbda991 --- /dev/null +++ b/egg/egg-buffer.c @@ -0,0 +1,566 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* egg-buffer.c - Generic data buffer, used by openssh, gnome-keyring + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ +#include "config.h" + +#include +#include + +#include "egg-buffer.h" + +#define DEFAULT_ALLOCATOR ((EggBufferAllocator)realloc) + +int +egg_buffer_init (EggBuffer *buffer, size_t reserve) +{ + return egg_buffer_init_full (buffer, reserve, NULL); +} + +int +egg_buffer_init_full (EggBuffer *buffer, size_t reserve, EggBufferAllocator allocator) +{ + memset (buffer, 0, sizeof (*buffer)); + + if (!allocator) + allocator = DEFAULT_ALLOCATOR; + if (reserve == 0) + reserve = 64; + + buffer->buf = (allocator) (NULL, reserve); + if (!buffer->buf) { + buffer->failures++; + return 0; + } + + buffer->len = 0; + buffer->allocated_len = reserve; + buffer->failures = 0; + buffer->allocator = allocator; + + return 1; +} + +void +egg_buffer_init_static (EggBuffer* buffer, unsigned char *buf, size_t len) +{ + memset (buffer, 0, sizeof (*buffer)); + + buffer->buf = buf; + buffer->len = len; + buffer->allocated_len = len; + buffer->failures = 0; + + /* A null allocator, and the buffer can't change in size */ + buffer->allocator = NULL; +} + +void +egg_buffer_init_allocated (EggBuffer *buffer, unsigned char *buf, size_t len, + EggBufferAllocator allocator) +{ + memset (buffer, 0, sizeof (*buffer)); + + if (!allocator) + allocator = DEFAULT_ALLOCATOR; + + buffer->buf = buf; + buffer->len = len; + buffer->allocated_len = len; + buffer->failures = 0; + buffer->allocator = allocator; +} + +void +egg_buffer_reset (EggBuffer *buffer) +{ + memset (buffer->buf, 0, buffer->allocated_len); + buffer->len = 0; + buffer->failures = 0; +} + +void +egg_buffer_uninit (EggBuffer *buffer) +{ + if (!buffer) + return; + + /* + * Free the memory block using allocator. If no allocator, + * then this memory is ownerd elsewhere and not to be freed. + */ + if (buffer->buf && buffer->allocator) + (buffer->allocator) (buffer->buf, 0); + + memset (buffer, 0, sizeof (*buffer)); +} + +int +egg_buffer_set_allocator (EggBuffer *buffer, EggBufferAllocator allocator) +{ + unsigned char *buf; + + if (!allocator) + allocator = DEFAULT_ALLOCATOR; + if (buffer->allocator == allocator) + return 1; + + /* Reallocate memory block using new allocator */ + buf = (allocator) (NULL, buffer->allocated_len); + if (!buf) + return 0; + + /* Copy stuff and free old memory */ + memcpy (buf, buffer->buf, buffer->allocated_len); + + /* If old wasn't static, then free it */ + if (buffer->allocator) + (buffer->allocator) (buffer->buf, 0); + + buffer->buf = buf; + buffer->allocator = allocator; + + return 1; +} + +int +egg_buffer_equal (EggBuffer *b1, EggBuffer *b2) +{ + if (b1->len != b2->len) + return 0; + return memcmp (b1->buf, b2->buf, b1->len) == 0; +} + +int +egg_buffer_reserve (EggBuffer *buffer, size_t len) +{ + unsigned char *newbuf; + size_t newlen; + + if (len < buffer->allocated_len) + return 1; + + /* Calculate a new length, minimize number of buffer allocations */ + newlen = buffer->allocated_len * 2; + if (len > newlen) + newlen += len; + + /* Memory owned elsewhere can't be reallocated */ + if (!buffer->allocator) { + buffer->failures++; + return 0; + } + + /* Reallocate built in buffer using allocator */ + newbuf = (buffer->allocator) (buffer->buf, newlen); + if (!newbuf) { + buffer->failures++; + return 0; + } + + buffer->buf = newbuf; + buffer->allocated_len = newlen; + + return 1; +} + +int +egg_buffer_resize (EggBuffer *buffer, size_t len) +{ + if (!egg_buffer_reserve (buffer, len)) + return 0; + + buffer->len = len; + return 1; +} + +unsigned char* +egg_buffer_add_empty (EggBuffer *buffer, size_t len) +{ + size_t pos = buffer->len; + if (!egg_buffer_reserve (buffer, buffer->len + len)) + return NULL; + buffer->len += len; + return buffer->buf + pos; +} + +int +egg_buffer_append (EggBuffer *buffer, const unsigned char *val, + size_t len) +{ + if (!egg_buffer_reserve (buffer, buffer->len + len)) + return 0; /* failures already incremented */ + memcpy (buffer->buf + buffer->len, val, len); + buffer->len += len; + return 1; +} + +int +egg_buffer_add_byte (EggBuffer *buffer, unsigned char val) +{ + if (!egg_buffer_reserve (buffer, buffer->len + 1)) + return 0; /* failures already incremented */ + buffer->buf[buffer->len] = val; + buffer->len++; + return 1; +} + +int +egg_buffer_get_byte (EggBuffer *buffer, size_t offset, + size_t *next_offset, unsigned char *val) +{ + unsigned char *ptr; + if (buffer->len < 1 || offset > buffer->len - 1) { + buffer->failures++; + return 0; + } + ptr = (unsigned char*)buffer->buf + offset; + if (val != NULL) + *val = *ptr; + if (next_offset != NULL) + *next_offset = offset + 1; + return 1; +} + +void +egg_buffer_encode_uint16 (unsigned char* buf, uint16_t val) +{ + buf[0] = (val >> 8) & 0xff; + buf[1] = (val >> 0) & 0xff; +} + +uint16_t +egg_buffer_decode_uint16 (unsigned char* buf) +{ + uint16_t val = buf[0] << 8 | buf[1]; + return val; +} + +int +egg_buffer_add_uint16 (EggBuffer *buffer, uint16_t val) +{ + if (!egg_buffer_reserve (buffer, buffer->len + 2)) + return 0; /* failures already incremented */ + buffer->len += 2; + egg_buffer_set_uint16 (buffer, buffer->len - 2, val); + return 1; +} + +int +egg_buffer_set_uint16 (EggBuffer *buffer, size_t offset, uint16_t val) +{ + unsigned char *ptr; + if (buffer->len < 2 || offset > buffer->len - 2) { + buffer->failures++; + return 0; + } + ptr = (unsigned char*)buffer->buf + offset; + egg_buffer_encode_uint16 (ptr, val); + return 1; +} + +int +egg_buffer_get_uint16 (EggBuffer *buffer, size_t offset, + size_t *next_offset, uint16_t *val) +{ + unsigned char *ptr; + if (buffer->len < 2 || offset > buffer->len - 2) { + buffer->failures++; + return 0; + } + ptr = (unsigned char*)buffer->buf + offset; + if (val != NULL) + *val = egg_buffer_decode_uint16 (ptr); + if (next_offset != NULL) + *next_offset = offset + 2; + return 1; +} + +void +egg_buffer_encode_uint32 (unsigned char* buf, uint32_t val) +{ + buf[0] = (val >> 24) & 0xff; + buf[1] = (val >> 16) & 0xff; + buf[2] = (val >> 8) & 0xff; + buf[3] = (val >> 0) & 0xff; +} + +uint32_t +egg_buffer_decode_uint32 (unsigned char* ptr) +{ + uint32_t val = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + return val; +} + +int +egg_buffer_add_uint32 (EggBuffer *buffer, uint32_t val) +{ + if (!egg_buffer_reserve (buffer, buffer->len + 4)) + return 0; /* failures already incremented */ + buffer->len += 4; + egg_buffer_set_uint32 (buffer, buffer->len - 4, val); + return 1; +} + +int +egg_buffer_set_uint32 (EggBuffer *buffer, size_t offset, uint32_t val) +{ + unsigned char *ptr; + if (buffer->len < 4 || offset > buffer->len - 4) { + buffer->failures++; + return 0; + } + ptr = (unsigned char*)buffer->buf + offset; + egg_buffer_encode_uint32 (ptr, val); + return 1; +} + +int +egg_buffer_get_uint32 (EggBuffer *buffer, size_t offset, size_t *next_offset, + uint32_t *val) +{ + unsigned char *ptr; + if (buffer->len < 4 || offset > buffer->len - 4) { + buffer->failures++; + return 0; + } + ptr = (unsigned char*)buffer->buf + offset; + if (val != NULL) + *val = egg_buffer_decode_uint32 (ptr); + if (next_offset != NULL) + *next_offset = offset + 4; + return 1; +} + +int +egg_buffer_add_uint64 (EggBuffer *buffer, uint64_t val) +{ + if (!egg_buffer_add_uint32 (buffer, ((val >> 32) & 0xffffffff))) + return 0; + return egg_buffer_add_uint32 (buffer, (val & 0xffffffff)); +} + +int +egg_buffer_get_uint64 (EggBuffer *buffer, size_t offset, + size_t *next_offset, uint64_t *val) +{ + uint32_t a, b; + if (!egg_buffer_get_uint32 (buffer, offset, &offset, &a)) + return 0; + if (!egg_buffer_get_uint32 (buffer, offset, &offset, &b)) + return 0; + if (val != NULL) + *val = ((uint64_t)a) << 32 | b; + if (next_offset != NULL) + *next_offset = offset; + return 1; +} + +int +egg_buffer_add_byte_array (EggBuffer *buffer, const unsigned char *val, + size_t len) +{ + if (val == NULL) + return egg_buffer_add_uint32 (buffer, 0xffffffff); + if (len >= 0x7fffffff) { + buffer->failures++; + return 0; + } + if (!egg_buffer_add_uint32 (buffer, len)) + return 0; + return egg_buffer_append (buffer, val, len); +} + +unsigned char* +egg_buffer_add_byte_array_empty (EggBuffer *buffer, size_t vlen) +{ + if (vlen >= 0x7fffffff) { + buffer->failures++; + return NULL; + } + if (!egg_buffer_add_uint32 (buffer, vlen)) + return NULL; + return egg_buffer_add_empty (buffer, vlen); +} + +int +egg_buffer_get_byte_array (EggBuffer *buffer, size_t offset, + size_t *next_offset, const unsigned char **val, + size_t *vlen) +{ + uint32_t len; + if (!egg_buffer_get_uint32 (buffer, offset, &offset, &len)) + return 0; + if (len == 0xffffffff) { + if (next_offset) + *next_offset = offset; + if (val) + *val = NULL; + if (vlen) + *vlen = 0; + return 1; + } else if (len >= 0x7fffffff) { + buffer->failures++; + return 0; + } + + if (buffer->len < len || offset > buffer->len - len) { + buffer->failures++; + return 0; + } + + if (val) + *val = buffer->buf + offset; + if (vlen) + *vlen = len; + if (next_offset) + *next_offset = offset + len; + + return 1; +} + +int +egg_buffer_add_string (EggBuffer *buffer, const char *str) +{ + if (str == NULL) { + return egg_buffer_add_uint32 (buffer, 0xffffffff); + } else { + size_t len = strlen (str); + if (len >= 0x7fffffff) + return 0; + if (!egg_buffer_add_uint32 (buffer, len)) + return 0; + return egg_buffer_append (buffer, (unsigned char*)str, len); + } +} + +int +egg_buffer_get_string (EggBuffer *buffer, size_t offset, size_t *next_offset, + char **str_ret, EggBufferAllocator allocator) +{ + uint32_t len; + + if (!allocator) + allocator = buffer->allocator; + if (!allocator) + allocator = DEFAULT_ALLOCATOR; + + if (!egg_buffer_get_uint32 (buffer, offset, &offset, &len)) { + return 0; + } + if (len == 0xffffffff) { + *next_offset = offset; + *str_ret = NULL; + return 1; + } else if (len >= 0x7fffffff) { + return 0; + } + + if (buffer->len < len || + offset > buffer->len - len) { + return 0; + } + + /* Make sure no null characters in string */ + if (memchr (buffer->buf + offset, 0, len) != NULL) + return 0; + + /* The passed allocator may be for non-pageable memory */ + *str_ret = (allocator) (NULL, len + 1); + if (!*str_ret) + return 0; + memcpy (*str_ret, buffer->buf + offset, len); + + /* Always zero terminate */ + (*str_ret)[len] = 0; + *next_offset = offset + len; + + return 1; +} + +int +egg_buffer_add_stringv (EggBuffer *buffer, const char** strv) +{ + const char **v; + uint32_t n = 0; + + if (!strv) + return 0; + + /* Add the number of strings coming */ + for (v = strv; *v; ++v) + ++n; + if (!egg_buffer_add_uint32 (buffer, n)) + return 0; + + /* Add the individual strings */ + for (v = strv; *v; ++v) { + if (!egg_buffer_add_string (buffer, *v)) + return 0; + } + + return 1; +} + +int +egg_buffer_get_stringv (EggBuffer *buffer, size_t offset, size_t *next_offset, + char ***strv_ret, EggBufferAllocator allocator) +{ + uint32_t n, i, j; + size_t len; + + if (!allocator) + allocator = buffer->allocator; + if (!allocator) + allocator = DEFAULT_ALLOCATOR; + + /* First the number of environment variable lines */ + if (!egg_buffer_get_uint32 (buffer, offset, &offset, &n)) + return 0; + + /* Then that number of strings */ + len = (n + 1) * sizeof (char*); + *strv_ret = (char**)(allocator) (NULL, len); + if (!*strv_ret) + return 0; + + /* All null strings */ + memset (*strv_ret, 0, len); + + for (i = 0; i < n; ++i) { + if (!egg_buffer_get_string (buffer, offset, &offset, + &((*strv_ret)[i]), allocator)) { + + /* Free all the strings on failure */ + for (j = 0; j < i; ++j) { + if ((*strv_ret)[j]) + (allocator) ((*strv_ret)[j], 0); + } + + return 0; + } + } + + if (next_offset != NULL) + *next_offset = offset; + + return 1; +} diff --git a/egg/egg-buffer.h b/egg/egg-buffer.h new file mode 100644 index 0000000..c937460 --- /dev/null +++ b/egg/egg-buffer.h @@ -0,0 +1,194 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* egg-buffer.h - Generic data buffer, used by openssh, gnome-keyring + + Copyright (C) 2007, Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#ifndef EGG_BUFFER_H +#define EGG_BUFFER_H + +#include +#include + +/* ------------------------------------------------------------------- + * EggBuffer + * + * IMPORTANT: This is pure vanila standard C, no glib. We need this + * because certain consumers of this protocol need to be built + * without linking in any special libraries. ie: the PKCS#11 module. + * + * Memory Allocation + * + * Callers can set their own allocator. If NULL is used then standard + * C library heap memory is used and failures will not be fatal. Memory + * failures will instead result in a zero return value or + * egg_buffer_has_error() returning one. + * + * If you use something like g_realloc as the allocator, then memory + * failures become fatal just like in a standard GTK program. + * + * Don't change the allocator manually in the EggBuffer structure. The + * egg_buffer_set_allocator() func will reallocate and handle things + * properly. + * + * Pointers into the Buffer + * + * Any write operation has the posibility of reallocating memory + * and invalidating any direct pointers into the buffer. + */ + +/* The allocator for the EggBuffer. This follows the realloc() syntax and logic */ +typedef void* (*EggBufferAllocator) (void* p, unsigned long len); + +typedef struct _EggBuffer { + unsigned char *buf; + size_t len; + size_t allocated_len; + int failures; + EggBufferAllocator allocator; +} EggBuffer; + +#define EGG_BUFFER_EMPTY { NULL, 0, 0, 0, NULL } + +int egg_buffer_init (EggBuffer *buffer, size_t reserve); + +int egg_buffer_init_full (EggBuffer *buffer, + size_t reserve, + EggBufferAllocator allocator); + +void egg_buffer_init_static (EggBuffer *buffer, + unsigned char *buf, + size_t len); + +void egg_buffer_init_allocated (EggBuffer *buffer, + unsigned char *buf, + size_t len, + EggBufferAllocator allocator); + +void egg_buffer_uninit (EggBuffer *buffer); + +int egg_buffer_set_allocator (EggBuffer *buffer, + EggBufferAllocator allocator); + +void egg_buffer_reset (EggBuffer *buffer); + +int egg_buffer_equal (EggBuffer *b1, + EggBuffer *b2); + +int egg_buffer_reserve (EggBuffer *buffer, + size_t len); + +int egg_buffer_resize (EggBuffer *buffer, + size_t len); + +int egg_buffer_append (EggBuffer *buffer, + const unsigned char *val, + size_t len); + +unsigned char* egg_buffer_add_empty (EggBuffer *buffer, + size_t len); + +int egg_buffer_add_byte (EggBuffer *buffer, + unsigned char val); + +int egg_buffer_get_byte (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + unsigned char *val); + +void egg_buffer_encode_uint32 (unsigned char* buf, + uint32_t val); + +uint32_t egg_buffer_decode_uint32 (unsigned char* buf); + +int egg_buffer_add_uint32 (EggBuffer *buffer, + uint32_t val); + +int egg_buffer_set_uint32 (EggBuffer *buffer, + size_t offset, + uint32_t val); + +int egg_buffer_get_uint32 (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + uint32_t *val); + +void egg_buffer_encode_uint16 (unsigned char* buf, + uint16_t val); + +uint16_t egg_buffer_decode_uint16 (unsigned char* buf); + +int egg_buffer_add_uint16 (EggBuffer *buffer, + uint16_t val); + +int egg_buffer_set_uint16 (EggBuffer *buffer, + size_t offset, + uint16_t val); + +int egg_buffer_get_uint16 (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + uint16_t *val); + +int egg_buffer_add_byte_array (EggBuffer *buffer, + const unsigned char *val, + size_t len); + +int egg_buffer_get_byte_array (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + const unsigned char **val, + size_t *vlen); + +unsigned char* egg_buffer_add_byte_array_empty (EggBuffer *buffer, + size_t vlen); + +int egg_buffer_add_string (EggBuffer *buffer, + const char *str); + +int egg_buffer_get_string (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + char **str_ret, + EggBufferAllocator allocator); + +int egg_buffer_add_stringv (EggBuffer *buffer, + const char** strv); + +int egg_buffer_get_stringv (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + char ***strv_ret, + EggBufferAllocator allocator); + +int egg_buffer_add_uint64 (EggBuffer *buffer, + uint64_t val); + +int egg_buffer_get_uint64 (EggBuffer *buffer, + size_t offset, + size_t *next_offset, + uint64_t *val); + +#define egg_buffer_length(b) ((b)->len) + +#define egg_buffer_has_error(b) ((b)->failures > 0) + +#endif /* EGG_BUFFER_H */ + diff --git a/egg/egg-secure-memory.c b/egg/egg-secure-memory.c new file mode 100644 index 0000000..39f6bf7 --- /dev/null +++ b/egg/egg-secure-memory.c @@ -0,0 +1,805 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* egg-secure-memory.h - library for allocating memory that is non-pageable + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +/* + * IMPORTANT: This is pure vanila standard C, no glib. We need this + * because certain consumers of this protocol need to be built + * without linking in any special libraries. ie: the PKCS#11 module. + */ + +#include "config.h" + +#include "egg-secure-memory.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Use this to force all memory through malloc + * for use with valgrind and the like + */ +#define FORCE_MALLOC_MEMORY 0 +#define FORCE_FALLBACK_MEMORY 0 + +#define DEBUG_SECURE_MEMORY 0 + +#if DEBUG_SECURE_MEMORY +#define DEBUG_ALLOC(msg, n) fprintf(stderr, "%s %lu bytes\n", msg, n); +#else +#define DEBUG_ALLOC(msg, n) +#endif + +#define DEFAULT_BLOCK_SIZE 16384 + +/* Use our own assert to guarantee no glib allocations */ +#ifndef ASSERT +#ifdef G_DISABLE_ASSERT +#define ASSERT(x) +#else +#define ASSERT(x) assert(x) +#endif +#endif + +#define DO_LOCK() \ + egg_memory_lock (); + +#define DO_UNLOCK() \ + egg_memory_unlock (); + +#define MEM_ALIGN (sizeof(void*) > sizeof(long) ? sizeof(void*) : sizeof(long)) + +/* ----------------------------------------------------------------------------- + * BLOCK SUBALLOCATION + */ + +/* suba - sub-allocate memory from larger chunk of memory + * Copyright (c) 2003 Michael B. Allen + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +typedef size_t ref_t; /* suba offset from start of memory to object */ + +#define SUBA_MAGIC "\xFF\x15\x15\x15SUBA" +#define CELL_MAGIC 0x7777CE11 +#define ALIGN(s) ((((s) / MEM_ALIGN) + (((s) % MEM_ALIGN) ? 1 : 0)) * MEM_ALIGN) +#define POFF (ALIGN(sizeof(size_t)) + ALIGN(sizeof(unsigned int))) +#define C2P(c) ((char *)(c) + POFF) +#define P2C(p) ((struct cell *)((char *)(p) - POFF)) +#define ISADJ(c1,c2) ((struct cell *)(C2P(c1) + (c1)->size) == (struct cell *)(c2)) +#define SREF(s,p) (ref_t)((char *)(p) - (char *)(s)) +#define SADR(s,r) (void *)((char *)(s) + (r)) +#define RECLAIM_DEPTH_MAX 2 + +struct allocator { + unsigned char magic[8]; /* suba header identifier */ + ref_t tail; /* offset to first cell in free list */ + size_t mincell; /* min cell size must be at least sizeof cell */ + size_t size; /* total size of memory area */ + size_t alloc_total; /* total bytes utilized from this allocator */ + size_t free_total; /* total bytes released from this allocator */ + size_t size_total; /* total bytes requested from this allocator */ + ref_t userref; +}; + +struct cell { + size_t size; + unsigned int magic; + ref_t next; /* reference to next cell in free list */ +}; + +static void* +suba_addr (const struct allocator *suba, const ref_t ref) +{ + if (suba && ref > 0 && ref <= suba->size) { + return (char *)suba + ref; + } + return NULL; +} + +static ref_t +suba_ref (const struct allocator *suba, const void *ptr) +{ + if (suba && ptr) { + ref_t ref = (char *)ptr - (char *)suba; + if (ref > 0 && ref <= suba->size) { + return ref; + } + } + return 0; +} + +static struct allocator * +suba_init (void *mem, size_t size, size_t mincell) +{ + struct allocator *suba = mem; + size_t hdrsiz; + struct cell *c; + + hdrsiz = ALIGN(sizeof *suba); + + ASSERT (mem != NULL); + ASSERT (size > (hdrsiz + POFF)); + + memset(suba, 0, hdrsiz); + memcpy(suba->magic, SUBA_MAGIC, 8); + suba->tail = hdrsiz; + suba->mincell = mincell < ALIGN (sizeof (*c)) ? ALIGN (sizeof (*c)) : ALIGN (mincell); + suba->size = size; + + c = suba_addr(suba, hdrsiz); + c->size = size - (hdrsiz + POFF); + c->next = suba->tail; + + return suba; +} + +static void * +suba_alloc(struct allocator *suba, size_t size) +{ + struct cell *c1, *c2, *c3; + size_t s = size; + + size = size < suba->mincell ? suba->mincell : ALIGN(size); + + c2 = SADR(suba, suba->tail); + for ( ;; ) { + c1 = c2; + if ((c2 = suba_addr(suba, c1->next)) == NULL) { + errno = EFAULT; + return NULL; + } + if (c2->size >= size) { + break; /* found a cell large enough */ + } + if (c1->next == suba->tail) { + return NULL; + } + } + + if ((c2->size - size) > suba->mincell) { + /* split new cell */ + c3 = (struct cell *)(C2P(c2) + size); + c3->size = c2->size - (size + POFF); + if (c1 == c2) { + c1 = c3; + } else { + c3->next = c2->next; + } + c1->next = SREF(suba, c3); + c2->size = size; + if (c2 == SADR(suba, suba->tail)) { + suba->tail = SREF(suba, c3); + } + } else if (c1->next == suba->tail) { + /* never use the last cell! */ + } else { + /* use the entire cell */ + c1->next = c2->next; + } + + suba->alloc_total += POFF + c2->size; + suba->size_total += s; + + c2->magic = CELL_MAGIC; + DEBUG_ALLOC ("gkr-secure-memory: allocated ", (unsigned long)size); + + /* TODO: Fix suba, so always allocates zero */ + memset (C2P(c2), 0, size); + + return C2P(c2); +} + +static void +suba_free(void *suba0, void *ptr) +{ + struct allocator *suba = suba0; + struct cell *c1, *c2, *c3; + volatile char *vp; + size_t len; + ref_t ref; + int j1, j2; + + if (!ptr) + return; + + c1 = SADR(suba, suba->tail); + + /* Find out what cell we're talking about */ + c2 = P2C(ptr); + if ((ref = suba_ref(suba, c2)) == 0) { + ASSERT(0 && "invalid memory pointer passed to gkr-secure-memory"); + return; + } + if (c2->magic != CELL_MAGIC) { + ASSERT(0 && "invalid memory pointer passed to gkr-secure-memory"); + return; + } + + /* Clear out memory */ + vp = (volatile char*)ptr; + len = c2->size; + while (len) { + *vp = 0xaa; + vp++; + len--; + } + + suba->free_total += POFF + c2->size; + suba->alloc_total -= (POFF + c2->size); + + c2->magic = 0; + DEBUG_ALLOC ("gkr-secure-memory: freed ", (unsigned long)c2->size); + + /* splice the cell back into the list */ + if (c2 > c1) { /* append to end of list */ + if (ISADJ(c1,c2)) { /* join with last cell */ + c1->size += POFF + c2->size; + return; + } + c2->next = c1->next; + suba->tail = c1->next = ref; + return; + } + + while (c1->next < ref) { /* find insertion point */ + c1 = SADR(suba, c1->next); + } + c3 = SADR(suba, c1->next); + + j1 = ISADJ(c1,c2); /* c1 and c2 need to be joined */ + j2 = ISADJ(c2,c3); /* c2 and c3 need to be joined */ + + if (j1) { + if (j2) { /* splice all three cells together */ + if (SREF(suba, c3) == suba->tail) { + suba->tail = SREF(suba, c1); + } + c1->next = c3->next; + c1->size += POFF + c3->size; + } + c1->size += POFF + c2->size; + } else { + if (j2) { + if (SREF(suba, c3) == suba->tail) { + suba->tail = ref; + } + c2->next = c3->next == SREF(suba, c3) ? ref : c3->next; + c2->size += POFF + c3->size; + } else { + c2->next = c1->next; + } + c1->next = ref; + } +} + +static void * +suba_realloc(struct allocator *suba, void *ptr, size_t size) +{ + struct cell *c; + void *p; + + if (ptr == NULL) + return suba_alloc(suba, size); + if (size == 0) { + suba_free(suba, ptr); + return NULL; + } + c = P2C(ptr); + if (c->size < size || (c->size - ALIGN(size)) > suba->mincell) { + p = suba_alloc(suba, size); + } else { + return ptr; + } + if (p) { + memcpy(p, ptr, c->size); + suba_free(suba, ptr); + } + + return p; +} + +static int +suba_print_cell(struct allocator *suba, const char *msg, struct cell *c) +{ + ref_t ref = suba_ref(suba, c); + if (ref >= ALIGN(sizeof *suba) && (ref + POFF + c->size) <= 10000000) { + fprintf(stderr, "%s: %8u-%-8u %8u %-8u\n", msg, + (unsigned int)ref, (unsigned int)(ref + POFF + c->size), + (unsigned int)c->size, (unsigned int)c->next); + } else { + fprintf(stderr, "%s: %8u-err %8u %-8u\n", msg, + (unsigned int)ref, (unsigned int)c->size, + (unsigned int)c->next); + return 0; + } + return 1; +} + +static int +suba_print_free_list(struct allocator *suba) +{ + struct cell *c; + char buf[10]; + int count = 0; + int ret = 1; + + c = suba_addr(suba, suba->tail); + while (c->next < suba->tail) { + c = suba_addr(suba, c->next); + sprintf(buf, "%d", count++); + if (!suba_print_cell(suba, buf, c)) { + ret = 0; + } + } + c = suba_addr(suba, c->next); + sprintf(buf, "%d", count++); + if (!suba_print_cell(suba, buf, c)) { + ret = 0; + } + + return ret; +} + +static size_t +suba_allocation_size (struct allocator *suba, void *ptr) +{ + struct cell *c = P2C(ptr); + if (c->magic != CELL_MAGIC) { + ASSERT(0 && "invalid memory pointer passed to gkr-secure-memory"); + return 0; + } + return c->size; +} + +/* ----------------------------------------------------------------------------- + * PAGE SOURCE -- Where blocks of locked memory pages come from. + */ + +static int lock_warning = 1; + +static void* +get_locked_pages (unsigned long *sz) +{ + void *pages; + unsigned long pgsize; + + ASSERT (sz); + ASSERT (*sz); + + /* Make sure sz is a multiple of the page size */ + pgsize = getpagesize (); + *sz = (*sz + pgsize -1) & ~(pgsize - 1); + +#if FORCE_MALLOC_MEMORY + pages = malloc (*sz); + memset (pages, 0, *sz); + lock_warning = 1; + return pages; + +#elif defined(HAVE_MLOCK) + pages = mmap (0, *sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (pages == MAP_FAILED) { + if (lock_warning) + fprintf (stderr, "couldn't map %lu bytes of private memory: %s\n", + *sz, strerror (errno)); + lock_warning = 0; + return NULL; + } + + if (mlock (pages, *sz) < 0) { + if (lock_warning && errno != EPERM) { + fprintf (stderr, "couldn't lock %lu bytes of private memory: %s\n", + *sz, strerror (errno)); + lock_warning = 0; + } + munmap (pages, *sz); + return NULL; + } + + DEBUG_ALLOC ("gkr-secure-memory: new block ", *sz); + + lock_warning = 1; + return pages; + +#else + if (lock_warning) + fprintf (stderr, "your system does not support private memory"); + lock_warning = 0; + return NULL; +#endif + +} + +static void +rel_locked_pages (void *pages, unsigned long sz) +{ + ASSERT (pages); + ASSERT (sz % getpagesize () == 0); + +#if FORCE_MALLOC_MEMORY + free (pages); + +#elif defined(HAVE_MLOCK) + if (munlock (pages, sz) < 0) + fprintf (stderr, "couldn't unlock private memory: %s\n", strerror (errno)); + + if (munmap (pages, sz) < 0) + fprintf (stderr, "couldn't unmap private anonymous memory: %s\n", strerror (errno)); + + DEBUG_ALLOC ("gkr-secure-memory: freed block ", sz); + +#else + ASSERT (FALSE); +#endif +} + +/* ----------------------------------------------------------------------------- + * MANAGE DIFFERENT BLOCKS + */ + +typedef struct _MemBlock { + unsigned long size; + struct allocator *suba; + struct _MemBlock *next; +} MemBlock; + +static MemBlock *most_recent_block = NULL; + +static MemBlock* +block_create (unsigned long size) +{ + MemBlock *bl; + void *blmem; + +#if FORCE_FALLBACK_MEMORY + /* We can force all all memory to be malloced */ + return NULL; +#endif + + size += sizeof (MemBlock); + + /* The size above is a minimum, we're free to go bigger */ + if (size < DEFAULT_BLOCK_SIZE) + size = DEFAULT_BLOCK_SIZE; + + blmem = get_locked_pages (&size); + if (!blmem) + return NULL; + + bl = (MemBlock*)blmem; + bl->size = size; + bl->suba = suba_init (((unsigned char*)blmem) + sizeof (MemBlock), + size - sizeof (MemBlock), 32); + ASSERT (bl->suba); + + bl->next = most_recent_block; + most_recent_block = bl; + + return bl; +} + +static void +block_destroy (MemBlock *bl) +{ + MemBlock *b; + + ASSERT (bl && bl->suba); + ASSERT (bl->size > 0); + ASSERT (bl->suba->alloc_total == 0); + + /* Is the most recent block, simple */ + if (bl == most_recent_block) { + most_recent_block = bl->next; + + /* Take it out of our list */ + } else { + for (b = most_recent_block; b; b = b->next) { + if (b->next == bl) { + b->next = bl->next; + break; + } + } + ASSERT (b != NULL && "couldn't find memory block in list"); + } + + /* Memory is all in one block, nothing fancy to free */ + rel_locked_pages(bl, bl->size); +} + +static int +block_belongs (MemBlock *bl, const void *p) +{ + ASSERT (bl); + ASSERT (bl->size > 0); + + /* This does not check for invalid memory */ + return ((char*)p) >= ((char*)bl) && + ((char*)p) < (((char*)bl) + bl->size); +} + +void* +egg_secure_alloc (unsigned long sz) +{ + return egg_secure_alloc_full (sz, GKR_SECURE_USE_FALLBACK); +} + +void* +egg_secure_alloc_full (unsigned long sz, int flags) +{ + MemBlock *bl; + void *p = NULL; + + if (sz > 0xFFFFFFFF / 2) { + fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", sz); + return NULL; + } + + DO_LOCK (); + + for (bl = most_recent_block; bl; bl = bl->next) { + p = suba_alloc (bl->suba, sz); + if (p) + break; + } + + /* None of the current blocks have space, allocate new */ + if (!p) { + bl = block_create (sz); + if (bl) { + p = suba_alloc (bl->suba, sz); + ASSERT (p); + } + } + + DO_UNLOCK (); + + if (!p && (flags & GKR_SECURE_USE_FALLBACK)) { + p = egg_memory_fallback (NULL, sz); + if (p) /* Our returned memory is always zeroed */ + memset (p, 0, sz); + } + + if (!p) + errno = ENOMEM; + + return p; +} + +void* +egg_secure_realloc (void *p, unsigned long sz) +{ + return egg_secure_realloc_full (p, sz, GKR_SECURE_USE_FALLBACK); +} + +void* +egg_secure_realloc_full (void *p, unsigned long sz, int flags) +{ + MemBlock *bl = NULL; + unsigned long oldsz = 0; + int donew = 0; + void *n = NULL; + + if (sz > 0xFFFFFFFF / 2) { + fprintf (stderr, "tried to allocate an insane amount of memory: %lu\n", sz); + ASSERT (0 && "tried to allocate an insane amount of memory"); + return NULL; + } + + if (p == NULL) + return egg_secure_alloc_full (sz, flags); + if (!sz) { + egg_secure_free_full (p, flags); + return NULL; + } + + DO_LOCK (); + + /* Find out where it belongs to */ + for (bl = most_recent_block; bl; bl = bl->next) { + if (block_belongs (bl, p)) { + oldsz = suba_allocation_size (bl->suba, p); + n = suba_realloc (bl->suba, p, sz); + break; + } + } + + /* If it didn't work we may need to allocate a new block */ + if (bl && !n) + donew = 1; + + if (bl && bl->suba->alloc_total == 0) + block_destroy (bl); + + DO_UNLOCK (); + + if (!bl) { + if ((flags & GKR_SECURE_USE_FALLBACK)) { + /* + * In this case we can't zero the returned memory, + * because we don't know what the block size was. + */ + return egg_memory_fallback (p, sz); + } else { + fprintf (stderr, "memory does not belong to gnome-keyring: 0x%08lx\n", (unsigned long)p); + ASSERT (0 && "memory does does not belong to gnome-keyring"); + return NULL; + } + } + + if (donew) { + n = egg_secure_alloc_full (sz, flags); + if (n) { + memcpy (n, p, oldsz); + egg_secure_free_full (p, flags); + } + } + + if (!n) + errno = ENOMEM; + + return n; +} + +void +egg_secure_free (void *p) +{ + egg_secure_free_full (p, GKR_SECURE_USE_FALLBACK); +} + +void +egg_secure_free_full (void *p, int flags) +{ + MemBlock *bl = NULL; + + DO_LOCK (); + + /* Find out where it belongs to */ + for (bl = most_recent_block; bl; bl = bl->next) { + if (block_belongs (bl, p)) { + suba_free (bl->suba, p); + break; + } + } + + if (bl && bl->suba->alloc_total == 0) + block_destroy (bl); + + DO_UNLOCK (); + + if (!bl) { + if ((flags & GKR_SECURE_USE_FALLBACK)) { + egg_memory_fallback (p, 0); + } else { + fprintf (stderr, "memory does not belong to gnome-keyring: 0x%08lx\n", (unsigned long)p); + ASSERT (0 && "memory does does not belong to gnome-keyring"); + } + } +} + +int +egg_secure_check (const void *p) +{ + MemBlock *bl = NULL; + + DO_LOCK (); + + /* Find out where it belongs to */ + for (bl = most_recent_block; bl; bl = bl->next) { + if (block_belongs (bl, p)) + break; + } + + DO_UNLOCK (); + + return bl == NULL ? 0 : 1; +} + +void +egg_secure_dump_blocks (void) +{ + MemBlock *bl = NULL; + + DO_LOCK (); + + /* Find out where it belongs to */ + for (bl = most_recent_block; bl; bl = bl->next) { + fprintf (stderr, "----------------------------------------------------\n"); + fprintf (stderr, " BLOCK at: 0x%08lx len: %lu\n", (unsigned long)bl, bl->size); + fprintf (stderr, "\n"); + suba_print_free_list (bl->suba); + } + + DO_UNLOCK (); +} + +char* +egg_secure_strdup (const char *str) +{ + unsigned long len; + char *res; + + if (!str) + return NULL; + + len = strlen (str) + 1; + res = (char*)egg_secure_alloc (len); + strcpy (res, str); + return res; +} + +void +egg_secure_strclear (char *str) +{ + volatile char *vp; + size_t len; + + if (!str) + return; + + vp = (volatile char*)str; + len = strlen (str); + while (len) { + *vp = 0xAA; + vp++; + len--; + } +} + +void +egg_secure_strfree (char *str) +{ + /* + * If we're using unpageable 'secure' memory, then the free call + * should zero out the memory, but because on certain platforms + * we may be using normal memory, zero it out here just in case. + */ + + egg_secure_strclear (str); + egg_secure_free_full (str, GKR_SECURE_USE_FALLBACK); +} diff --git a/egg/egg-secure-memory.h b/egg/egg-secure-memory.h new file mode 100644 index 0000000..f4ddf3c --- /dev/null +++ b/egg/egg-secure-memory.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* egg-secure-memory.h - library for allocating memory that is non-pageable + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#ifndef EGG_SECURE_MEMORY_H +#define EGG_SECURE_MEMORY_H + +/* ------------------------------------------------------------------- + * Low Level Secure Memory + * + * IMPORTANT: This is pure vanila standard C, no glib. We need this + * because certain consumers of this protocol need to be built + * without linking in any special libraries. ie: the PKCS#11 module. + * + * Thread locking + * + * In order to use these functions in a module the following functions + * must be defined somewhere, and provide appropriate locking for + * secure memory between threads: + */ + +extern void egg_memory_lock (void); + +extern void egg_memory_unlock (void); + +/* + * Allocation Fallbacks + * + * If we cannot allocate secure memory, then this function + * (defined elsewhere) will be called which has a chance to + * allocate other memory abort or do whatever. + * + * Same call semantics as realloc with regard to NULL and zeros + */ +extern void* egg_memory_fallback (void *p, unsigned long sz); + + +/* + * Main functionality + * + * Allocations return NULL on failure. + */ + +#define GKR_SECURE_USE_FALLBACK 0x0001 + +void* egg_secure_alloc (unsigned long sz); + +void* egg_secure_alloc_full (unsigned long, int flags); + +void* egg_secure_realloc (void *p, unsigned long sz); + +void* egg_secure_realloc_full (void *p, unsigned long sz, int fallback); + +void egg_secure_free (void* p); + +void egg_secure_free_full (void* p, int fallback); + +int egg_secure_check (const void* p); + +void egg_secure_dump_blocks (void); + +char* egg_secure_strdup (const char *str); + +void egg_secure_strclear (char *str); + +void egg_secure_strfree (char *str); + +#endif /* EGG_SECURE_MEMORY_H */ diff --git a/egg/pk.asn b/egg/pk.asn new file mode 100644 index 0000000..f347c19 --- /dev/null +++ b/egg/pk.asn @@ -0,0 +1,103 @@ +PK { } + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +-- This file contains parts of PKCS-1 structures and some stuff +-- required for DSA keys. + +RSAPublicKey ::= SEQUENCE { + modulus INTEGER, -- n + publicExponent INTEGER -- e +} + +-- +-- Representation of RSA private key with information for the +-- CRT algorithm. +-- +RSAPrivateKey ::= SEQUENCE { + version Version, + modulus INTEGER, -- (Usually large) n + publicExponent INTEGER, -- (Usually small) e + privateExponent INTEGER, -- (Usually large) d + prime1 INTEGER, -- (Usually large) p + prime2 INTEGER, -- (Usually large) q + exponent1 INTEGER, -- (Usually large) d mod (p-1) + exponent2 INTEGER, -- (Usually large) d mod (q-1) + coefficient INTEGER, -- (Usually large) (inverse of q) mod p + otherPrimeInfos OtherPrimeInfos OPTIONAL +} + +Version ::= INTEGER { two-prime(0), multi(1) } +-- version must be multi if otherPrimeInfos present -- + +OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + +OtherPrimeInfo ::= SEQUENCE { + prime INTEGER, -- ri + exponent INTEGER, -- di + coefficient INTEGER -- ti +} + +-- for signature calculation +-- added by nmav + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL +} + -- contains a value of the type + -- registered for use with the + -- algorithm object identifier value + +DigestInfo ::= SEQUENCE { + digestAlgorithm DigestAlgorithmIdentifier, + digest Digest +} + +DigestAlgorithmIdentifier ::= AlgorithmIdentifier + +Digest ::= OCTET STRING + +DSAPublicPart ::= INTEGER + +DSAPublicKey ::= SEQUENCE { + version INTEGER, -- should be zero + p INTEGER, + q INTEGER, + g INTEGER, + Y INTEGER -- public +} + +DSAParameters ::= SEQUENCE { + p INTEGER, + q INTEGER, + g INTEGER +} + +DSASignatureValue ::= SEQUENCE { + r INTEGER, + s INTEGER +} + +DSAPrivatePart ::= INTEGER + +DSAPrivateKey ::= SEQUENCE { + version INTEGER, -- should be zero + p INTEGER, + q INTEGER, + g INTEGER, + Y INTEGER, -- public + priv INTEGER +} + +-- from PKCS#3 +DHParameter ::= SEQUENCE { + prime INTEGER, -- p + base INTEGER, -- g + privateValueLength INTEGER OPTIONAL +} + + +END diff --git a/egg/pkix.asn b/egg/pkix.asn new file mode 100644 index 0000000..1e5d657 --- /dev/null +++ b/egg/pkix.asn @@ -0,0 +1,1230 @@ + +PKIX1 { } + +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +-- This contains both PKIX1Implicit88 and RFC2630 ASN.1 modules. + +-- ISO arc for standard certificate and CRL extensions + +id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} + + +-- authority key identifier OID and syntax + +id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } + +AuthorityKeyIdentifier ::= SEQUENCE { + keyIdentifier [0] KeyIdentifier OPTIONAL, + authorityCertIssuer [1] GeneralNames OPTIONAL, + authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } + -- authorityCertIssuer and authorityCertSerialNumber shall both + -- be present or both be absgent + +KeyIdentifier ::= OCTET STRING + +-- subject key identifier OID and syntax + +id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } + +SubjectKeyIdentifier ::= KeyIdentifier + +-- key usage extension OID and syntax + +id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } + +KeyUsage ::= BIT STRING { + digitalSignature (0), + nonRepudiation (1), + keyEncipherment (2), + dataEncipherment (3), + keyAgreement (4), + keyCertSign (5), + cRLSign (6), + encipherOnly (7), + decipherOnly (8) } + +-- private key usage period extension OID and syntax + +id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } + +PrivateKeyUsagePeriod ::= SEQUENCE { + notBefore [0] GeneralizedTime OPTIONAL, + notAfter [1] GeneralizedTime OPTIONAL } + -- either notBefore or notAfter shall be present + +-- certificate policies extension OID and syntax + +id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } + +CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + +PolicyInformation ::= SEQUENCE { + policyIdentifier CertPolicyId, + policyQualifiers SEQUENCE SIZE (1..MAX) OF + PolicyQualifierInfo OPTIONAL } + +CertPolicyId ::= OBJECT IDENTIFIER + +PolicyQualifierInfo ::= SEQUENCE { + policyQualifierId PolicyQualifierId, + qualifier ANY DEFINED BY policyQualifierId } + +-- Implementations that recognize additional policy qualifiers shall +-- augment the following definition for PolicyQualifierId + +PolicyQualifierId ::= + OBJECT IDENTIFIER -- ( id-qt-cps | id-qt-unotice ) + +-- CPS pointer qualifier + +CPSuri ::= IA5String + +-- user notice qualifier + +UserNotice ::= SEQUENCE { + noticeRef NoticeReference OPTIONAL, + explicitText DisplayText OPTIONAL} + +NoticeReference ::= SEQUENCE { + organization DisplayText, + noticeNumbers SEQUENCE OF INTEGER } + +DisplayText ::= CHOICE { + visibleString VisibleString (SIZE (1..200)), + bmpString BMPString (SIZE (1..200)), + utf8String UTF8String (SIZE (1..200)) } + +-- policy mapping extension OID and syntax + +id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } + +PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { + issuerDomainPolicy CertPolicyId, + subjectDomainPolicy CertPolicyId } + +-- subject alternative name extension OID and syntax + +-- Directory string type -- + +DirectoryString ::= CHOICE { + teletexString TeletexString (SIZE (1..MAX)), + printableString PrintableString (SIZE (1..MAX)), + universalString UniversalString (SIZE (1..MAX)), + utf8String UTF8String (SIZE (1..MAX)), + bmpString BMPString (SIZE(1..MAX)), + -- IA5String is added here to handle old UID encoded as ia5String -- + -- See tests/userid/ for more information. It shouldn't be here, -- + -- so if it causes problems, considering dropping it. -- + ia5String IA5String (SIZE(1..MAX)) } + +id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } + +SubjectAltName ::= GeneralNames + +GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + +GeneralName ::= CHOICE { + otherName [0] AnotherName, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ORAddress, +-- Changed to work with the libtasn1 parser. + directoryName [4] EXPLICIT RDNSequence, --Name, + ediPartyName [5] EDIPartyName, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER } + +-- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as +-- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax + +AnotherName ::= SEQUENCE { + type-id OBJECT IDENTIFIER, + value [0] EXPLICIT ANY DEFINED BY type-id } + +EDIPartyName ::= SEQUENCE { + nameAssigner [0] DirectoryString OPTIONAL, + partyName [1] DirectoryString } + +-- issuer alternative name extension OID and syntax + +id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } + +IssuerAltName ::= GeneralNames + +id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } + +SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute + +-- basic constraints extension OID and syntax + +id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } + +BasicConstraints ::= SEQUENCE { + cA BOOLEAN DEFAULT FALSE, + pathLenConstraint INTEGER (0..MAX) OPTIONAL } + +-- name constraints extension OID and syntax + +id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } + +NameConstraints ::= SEQUENCE { + permittedSubtrees [0] GeneralSubtrees OPTIONAL, + excludedSubtrees [1] GeneralSubtrees OPTIONAL } + +GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + +GeneralSubtree ::= SEQUENCE { + base GeneralName, + minimum [0] BaseDistance DEFAULT 0, + maximum [1] BaseDistance OPTIONAL } + +BaseDistance ::= INTEGER (0..MAX) + +-- policy constraints extension OID and syntax + +id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } + +PolicyConstraints ::= SEQUENCE { + requireExplicitPolicy [0] SkipCerts OPTIONAL, + inhibitPolicyMapping [1] SkipCerts OPTIONAL } + +SkipCerts ::= INTEGER (0..MAX) + +-- CRL distribution points extension OID and syntax + +id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} + +CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + +DistributionPoint ::= SEQUENCE { + distributionPoint [0] EXPLICIT DistributionPointName OPTIONAL, + reasons [1] ReasonFlags OPTIONAL, + cRLIssuer [2] GeneralNames OPTIONAL +} + +DistributionPointName ::= CHOICE { + fullName [0] GeneralNames, + nameRelativeToCRLIssuer [1] RelativeDistinguishedName +} + +ReasonFlags ::= BIT STRING { + unused (0), + keyCompromise (1), + cACompromise (2), + affiliationChanged (3), + superseded (4), + cessationOfOperation (5), + certificateHold (6), + privilegeWithdrawn (7), + aACompromise (8) } + +-- extended key usage extension OID and syntax + +id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} + +ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + +KeyPurposeId ::= OBJECT IDENTIFIER + +-- extended key purpose OIDs +id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } +id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } +id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } +id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } +id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } +id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } + +-- authority info access + +id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } + +AuthorityInfoAccessSyntax ::= + SEQUENCE SIZE (1..MAX) OF AccessDescription + +AccessDescription ::= SEQUENCE { + accessMethod OBJECT IDENTIFIER, + accessLocation GeneralName } + +-- CRL number extension OID and syntax + +id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } + +CRLNumber ::= INTEGER (0..MAX) + +-- issuing distribution point extension OID and syntax + +id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } + +IssuingDistributionPoint ::= SEQUENCE { + distributionPoint [0] DistributionPointName OPTIONAL, + onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, + onlySomeReasons [3] ReasonFlags OPTIONAL, + indirectCRL [4] BOOLEAN DEFAULT FALSE } + + +id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } + +-- deltaCRLIndicator ::= BaseCRLNumber + +BaseCRLNumber ::= CRLNumber + +-- CRL reasons extension OID and syntax + +id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 } + +CRLReason ::= ENUMERATED { + unspecified (0), + keyCompromise (1), + cACompromise (2), + affiliationChanged (3), + superseded (4), + cessationOfOperation (5), + certificateHold (6), + removeFromCRL (8) } + +-- certificate issuer CRL entry extension OID and syntax + +id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } + +CertificateIssuer ::= GeneralNames + +-- hold instruction extension OID and syntax + +id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 } + +HoldInstructionCode ::= OBJECT IDENTIFIER + +-- ANSI x9 holdinstructions + +-- ANSI x9 arc holdinstruction arc +holdInstruction OBJECT IDENTIFIER ::= + {joint-iso-itu-t(2) member-body(2) us(840) x9cm(10040) 2} + +-- ANSI X9 holdinstructions referenced by this standard +id-holdinstruction-none OBJECT IDENTIFIER ::= + {holdInstruction 1} -- deprecated +id-holdinstruction-callissuer OBJECT IDENTIFIER ::= + {holdInstruction 2} +id-holdinstruction-reject OBJECT IDENTIFIER ::= + {holdInstruction 3} + +-- invalidity date CRL entry extension OID and syntax + +id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 } + +InvalidityDate ::= GeneralizedTime + + +-- -------------------------------------- +-- EXPLICIT +-- -------------------------------------- + +-- UNIVERSAL Types defined in '93 and '98 ASN.1 +-- but required by this specification + +VisibleString ::= [UNIVERSAL 26] IMPLICIT OCTET STRING + +NumericString ::= [UNIVERSAL 18] IMPLICIT OCTET STRING + +IA5String ::= [UNIVERSAL 22] IMPLICIT OCTET STRING + +TeletexString ::= [UNIVERSAL 20] IMPLICIT OCTET STRING + +PrintableString ::= [UNIVERSAL 19] IMPLICIT OCTET STRING + +UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING + -- UniversalString is defined in ASN.1:1993 + +BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING + -- BMPString is the subtype of UniversalString and models + -- the Basic Multilingual Plane of ISO/IEC/ITU 10646-1 + +UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING + -- The content of this type conforms to RFC 2279. + + +-- PKIX specific OIDs + +id-pkix OBJECT IDENTIFIER ::= + { iso(1) identified-organization(3) dod(6) internet(1) + security(5) mechanisms(5) pkix(7) } + +-- PKIX arcs + +id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } + -- arc for private certificate extensions +id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } + -- arc for policy qualifier types +id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } + -- arc for extended key purpose OIDS +id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } + -- arc for access descriptors + +-- policyQualifierIds for Internet policy qualifiers + +id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } + -- OID for CPS qualifier +id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } + -- OID for user notice qualifier + +-- access descriptor definitions + +id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } +id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } + +-- attribute data types -- + +Attribute ::= SEQUENCE { + type AttributeType, + values SET OF AttributeValue + -- at least one value is required -- +} + +AttributeType ::= OBJECT IDENTIFIER + +AttributeValue ::= ANY DEFINED BY type + +AttributeTypeAndValue ::= SEQUENCE { + type AttributeType, + value AttributeValue } + +-- suggested naming attributes: Definition of the following +-- information object set may be augmented to meet local +-- requirements. Note that deleting members of the set may +-- prevent interoperability with conforming implementations. +-- presented in pairs: the AttributeType followed by the +-- type definition for the corresponding AttributeValue + +-- Arc for standard naming attributes +id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} + +-- Attributes of type NameDirectoryString +id-at-initials AttributeType ::= { id-at 43 } +X520initials ::= DirectoryString + +id-at-generationQualifier AttributeType ::= { id-at 44 } +X520generationQualifier ::= DirectoryString + +id-at-surname AttributeType ::= { id-at 4 } +X520surName ::= DirectoryString + +id-at-givenName AttributeType ::= { id-at 42 } +X520givenName ::= DirectoryString + +id-at-name AttributeType ::= { id-at 41 } +X520name ::= DirectoryString + +id-at-commonName AttributeType ::= {id-at 3} +X520CommonName ::= DirectoryString + +id-at-localityName AttributeType ::= {id-at 7} +X520LocalityName ::= DirectoryString + +id-at-stateOrProvinceName AttributeType ::= {id-at 8} +X520StateOrProvinceName ::= DirectoryString + +id-at-organizationName AttributeType ::= {id-at 10} +X520OrganizationName ::= DirectoryString + +id-at-organizationalUnitName AttributeType ::= {id-at 11} +X520OrganizationalUnitName ::= DirectoryString + +id-at-title AttributeType ::= {id-at 12} +X520Title ::= DirectoryString + +id-at-description AttributeType ::= {id-at 13} +X520Description ::= DirectoryString + +id-at-dnQualifier AttributeType ::= {id-at 46} +X520dnQualifier ::= PrintableString + +id-at-countryName AttributeType ::= {id-at 6} +X520countryName ::= PrintableString (SIZE (2)) -- IS 3166 codes + +id-at-serialNumber AttributeType ::= {id-at 5} +X520serialNumber ::= PrintableString + +id-at-telephoneNumber AttributeType ::= {id-at 20} +X520telephoneNumber ::= PrintableString + +id-at-facsimileTelephoneNumber AttributeType ::= {id-at 23} +X520facsimileTelephoneNumber ::= PrintableString + +id-at-pseudonym AttributeType ::= {id-at 65} +X520pseudonym ::= DirectoryString + +id-at-name AttributeType ::= {id-at 41} +X520name ::= DirectoryString + +id-at-streetAddress AttributeType ::= {id-at 9} +X520streetAddress ::= DirectoryString + +id-at-postalAddress AttributeType ::= {id-at 16} +X520postalAddress ::= PostalAddress + +PostalAddress ::= SEQUENCE OF DirectoryString + + + -- Legacy attributes + +pkcs OBJECT IDENTIFIER ::= + { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) } + +pkcs-9 OBJECT IDENTIFIER ::= + { pkcs 9 } + + +emailAddress AttributeType ::= { pkcs-9 1 } + +Pkcs9email ::= IA5String (SIZE (1..ub-emailaddress-length)) + +-- naming data types -- + +Name ::= CHOICE { -- only one possibility for now -- + rdnSequence RDNSequence } + +RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + +DistinguishedName ::= RDNSequence + +RelativeDistinguishedName ::= + SET SIZE (1 .. MAX) OF AttributeTypeAndValue + + + +-- -------------------------------------------------------- +-- certificate and CRL specific structures begin here +-- -------------------------------------------------------- + +Certificate ::= SEQUENCE { + tbsCertificate TBSCertificate, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING } + +TBSCertificate ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + serialNumber CertificateSerialNumber, + signature AlgorithmIdentifier, + issuer Name, + validity Validity, + subject Name, + subjectPublicKeyInfo SubjectPublicKeyInfo, + issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + extensions [3] EXPLICIT Extensions OPTIONAL + -- If present, version shall be v3 -- +} + +Version ::= INTEGER { v1(0), v2(1), v3(2) } + +CertificateSerialNumber ::= INTEGER + +Validity ::= SEQUENCE { + notBefore Time, + notAfter Time } + +Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + +UniqueIdentifier ::= BIT STRING + +SubjectPublicKeyInfo ::= SEQUENCE { + algorithm AlgorithmIdentifier, + subjectPublicKey BIT STRING } + +Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + +Extension ::= SEQUENCE { + extnID OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING } + + +-- ------------------------------------------ +-- CRL structures +-- ------------------------------------------ + +CertificateList ::= SEQUENCE { + tbsCertList TBSCertList, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING } + +TBSCertList ::= SEQUENCE { + version Version OPTIONAL, + -- if present, shall be v2 + signature AlgorithmIdentifier, + issuer Name, + thisUpdate Time, + nextUpdate Time OPTIONAL, + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, shall be v2 + } OPTIONAL, + crlExtensions [0] EXPLICIT Extensions OPTIONAL + -- if present, shall be v2 -- +} + +-- Version, Time, CertificateSerialNumber, and Extensions were +-- defined earlier for use in the certificate structure + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + -- contains a value of the type + -- registered for use with the + -- algorithm object identifier value + +-- Algorithm OIDs and parameter structures + +pkcs-1 OBJECT IDENTIFIER ::= { + pkcs 1 } + +rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } + +md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } + +md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } + +sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } + +id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 } + +Dss-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER +} + +dhpublicnumber OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 } + +DomainParameters ::= SEQUENCE { + p INTEGER, -- odd prime, p=jq +1 + g INTEGER, -- generator, g + q INTEGER, -- factor of p-1 + j INTEGER OPTIONAL, -- subgroup factor, j>= 2 + validationParms ValidationParms OPTIONAL } + +ValidationParms ::= SEQUENCE { + seed BIT STRING, + pgenCounter INTEGER } + +id-dsa OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 } + +Dss-Parms ::= SEQUENCE { + p INTEGER, + q INTEGER, + g INTEGER } + +-- x400 address syntax starts here +-- OR Names + +ORAddress ::= SEQUENCE { + built-in-standard-attributes BuiltInStandardAttributes, + built-in-domain-defined-attributes + BuiltInDomainDefinedAttributes OPTIONAL, + -- see also teletex-domain-defined-attributes + extension-attributes ExtensionAttributes OPTIONAL } +-- The OR-address is semantically absent from the OR-name if the +-- built-in-standard-attribute sequence is empty and the +-- built-in-domain-defined-attributes and extension-attributes are +-- both omitted. + +-- Built-in Standard Attributes + +BuiltInStandardAttributes ::= SEQUENCE { + country-name CountryName OPTIONAL, + administration-domain-name AdministrationDomainName OPTIONAL, + network-address [0] EXPLICIT NetworkAddress OPTIONAL, + -- see also extended-network-address + terminal-identifier [1] EXPLICIT TerminalIdentifier OPTIONAL, + private-domain-name [2] EXPLICIT PrivateDomainName OPTIONAL, + organization-name [3] EXPLICIT OrganizationName OPTIONAL, + -- see also teletex-organization-name + numeric-user-identifier [4] EXPLICIT NumericUserIdentifier OPTIONAL, + personal-name [5] EXPLICIT PersonalName OPTIONAL, + -- see also teletex-personal-name + organizational-unit-names [6] EXPLICIT OrganizationalUnitNames OPTIONAL + -- see also teletex-organizational-unit-names -- +} + +CountryName ::= [APPLICATION 1] CHOICE { + x121-dcc-code NumericString + (SIZE (ub-country-name-numeric-length)), + iso-3166-alpha2-code PrintableString + (SIZE (ub-country-name-alpha-length)) } + +AdministrationDomainName ::= [APPLICATION 2] EXPLICIT CHOICE { + numeric NumericString (SIZE (0..ub-domain-name-length)), + printable PrintableString (SIZE (0..ub-domain-name-length)) } + +NetworkAddress ::= X121Address -- see also extended-network-address + +X121Address ::= NumericString (SIZE (1..ub-x121-address-length)) + +TerminalIdentifier ::= PrintableString (SIZE (1..ub-terminal-id-length)) + +PrivateDomainName ::= CHOICE { + numeric NumericString (SIZE (1..ub-domain-name-length)), + printable PrintableString (SIZE (1..ub-domain-name-length)) } + +OrganizationName ::= PrintableString + (SIZE (1..ub-organization-name-length)) +-- see also teletex-organization-name + +NumericUserIdentifier ::= NumericString + (SIZE (1..ub-numeric-user-id-length)) + +PersonalName ::= SET { + surname [0] PrintableString (SIZE (1..ub-surname-length)), + given-name [1] PrintableString + (SIZE (1..ub-given-name-length)) OPTIONAL, + initials [2] PrintableString (SIZE (1..ub-initials-length)) OPTIONAL, + generation-qualifier [3] PrintableString + (SIZE (1..ub-generation-qualifier-length)) OPTIONAL } +-- see also teletex-personal-name + +OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units) + OF OrganizationalUnitName +-- see also teletex-organizational-unit-names + +OrganizationalUnitName ::= PrintableString (SIZE + (1..ub-organizational-unit-name-length)) + +-- Built-in Domain-defined Attributes + +BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE + (1..ub-domain-defined-attributes) OF + BuiltInDomainDefinedAttribute + +BuiltInDomainDefinedAttribute ::= SEQUENCE { + type PrintableString (SIZE + (1..ub-domain-defined-attribute-type-length)), + value PrintableString (SIZE + (1..ub-domain-defined-attribute-value-length))} + +-- Extension Attributes + +ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF + ExtensionAttribute + +ExtensionAttribute ::= SEQUENCE { + extension-attribute-type [0] EXPLICIT INTEGER (0..ub-extension-attributes), + extension-attribute-value [1] EXPLICIT + ANY DEFINED BY extension-attribute-type } + +-- Extension types and attribute values +-- + +common-name INTEGER ::= 1 + +CommonName ::= PrintableString (SIZE (1..ub-common-name-length)) + +teletex-common-name INTEGER ::= 2 + +TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length)) + +teletex-organization-name INTEGER ::= 3 + +TeletexOrganizationName ::= + TeletexString (SIZE (1..ub-organization-name-length)) + +teletex-personal-name INTEGER ::= 4 + +TeletexPersonalName ::= SET { + surname [0] EXPLICIT TeletexString (SIZE (1..ub-surname-length)), + given-name [1] EXPLICIT TeletexString + (SIZE (1..ub-given-name-length)) OPTIONAL, + initials [2] EXPLICIT TeletexString (SIZE (1..ub-initials-length)) OPTIONAL, + generation-qualifier [3] EXPLICIT TeletexString (SIZE + (1..ub-generation-qualifier-length)) OPTIONAL } + +teletex-organizational-unit-names INTEGER ::= 5 + +TeletexOrganizationalUnitNames ::= SEQUENCE SIZE + (1..ub-organizational-units) OF TeletexOrganizationalUnitName + +TeletexOrganizationalUnitName ::= TeletexString + (SIZE (1..ub-organizational-unit-name-length)) + +pds-name INTEGER ::= 7 + +PDSName ::= PrintableString (SIZE (1..ub-pds-name-length)) + +physical-delivery-country-name INTEGER ::= 8 + +PhysicalDeliveryCountryName ::= CHOICE { + x121-dcc-code NumericString (SIZE (ub-country-name-numeric-length)), + iso-3166-alpha2-code PrintableString + (SIZE (ub-country-name-alpha-length)) } + +postal-code INTEGER ::= 9 + +PostalCode ::= CHOICE { + numeric-code NumericString (SIZE (1..ub-postal-code-length)), + printable-code PrintableString (SIZE (1..ub-postal-code-length)) } + +physical-delivery-office-name INTEGER ::= 10 + +PhysicalDeliveryOfficeName ::= PDSParameter + +physical-delivery-office-number INTEGER ::= 11 + +PhysicalDeliveryOfficeNumber ::= PDSParameter + +extension-OR-address-components INTEGER ::= 12 + +ExtensionORAddressComponents ::= PDSParameter + +physical-delivery-personal-name INTEGER ::= 13 + +PhysicalDeliveryPersonalName ::= PDSParameter + +physical-delivery-organization-name INTEGER ::= 14 + +PhysicalDeliveryOrganizationName ::= PDSParameter + +extension-physical-delivery-address-components INTEGER ::= 15 + +ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter + +unformatted-postal-address INTEGER ::= 16 + +UnformattedPostalAddress ::= SET { + printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines) OF + PrintableString (SIZE (1..ub-pds-parameter-length)) OPTIONAL, + teletex-string TeletexString + (SIZE (1..ub-unformatted-address-length)) OPTIONAL } + +street-address INTEGER ::= 17 + +StreetAddress ::= PDSParameter + +post-office-box-address INTEGER ::= 18 + +PostOfficeBoxAddress ::= PDSParameter + +poste-restante-address INTEGER ::= 19 + +PosteRestanteAddress ::= PDSParameter + +unique-postal-name INTEGER ::= 20 + +UniquePostalName ::= PDSParameter + +local-postal-attributes INTEGER ::= 21 + +LocalPostalAttributes ::= PDSParameter + +PDSParameter ::= SET { + printable-string PrintableString + (SIZE(1..ub-pds-parameter-length)) OPTIONAL, + teletex-string TeletexString + (SIZE(1..ub-pds-parameter-length)) OPTIONAL } + +extended-network-address INTEGER ::= 22 + +ExtendedNetworkAddress ::= CHOICE { + e163-4-address SEQUENCE { + number [0] EXPLICIT NumericString (SIZE (1..ub-e163-4-number-length)), + sub-address [1] EXPLICIT NumericString + (SIZE (1..ub-e163-4-sub-address-length)) OPTIONAL }, + psap-address [0] EXPLICIT PresentationAddress } + +PresentationAddress ::= SEQUENCE { + pSelector [0] EXPLICIT OCTET STRING OPTIONAL, + sSelector [1] EXPLICIT OCTET STRING OPTIONAL, + tSelector [2] EXPLICIT OCTET STRING OPTIONAL, + nAddresses [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING } + +terminal-type INTEGER ::= 23 + +TerminalType ::= INTEGER { + telex (3), + teletex (4), + g3-facsimile (5), + g4-facsimile (6), + ia5-terminal (7), + videotex (8) } -- (0..ub-integer-options) + +-- Extension Domain-defined Attributes + +teletex-domain-defined-attributes INTEGER ::= 6 + +TeletexDomainDefinedAttributes ::= SEQUENCE SIZE + (1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute + +TeletexDomainDefinedAttribute ::= SEQUENCE { + type TeletexString + (SIZE (1..ub-domain-defined-attribute-type-length)), + value TeletexString + (SIZE (1..ub-domain-defined-attribute-value-length)) } + +-- specifications of Upper Bounds shall be regarded as mandatory +-- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter +-- Upper Bounds + +-- Upper Bounds +ub-name INTEGER ::= 32768 +ub-common-name INTEGER ::= 64 +ub-locality-name INTEGER ::= 128 +ub-state-name INTEGER ::= 128 +ub-organization-name INTEGER ::= 64 +ub-organizational-unit-name INTEGER ::= 64 +ub-title INTEGER ::= 64 +ub-match INTEGER ::= 128 + +ub-emailaddress-length INTEGER ::= 128 + +ub-common-name-length INTEGER ::= 64 +ub-country-name-alpha-length INTEGER ::= 2 +ub-country-name-numeric-length INTEGER ::= 3 +ub-domain-defined-attributes INTEGER ::= 4 +ub-domain-defined-attribute-type-length INTEGER ::= 8 +ub-domain-defined-attribute-value-length INTEGER ::= 128 +ub-domain-name-length INTEGER ::= 16 +ub-extension-attributes INTEGER ::= 256 +ub-e163-4-number-length INTEGER ::= 15 +ub-e163-4-sub-address-length INTEGER ::= 40 +ub-generation-qualifier-length INTEGER ::= 3 +ub-given-name-length INTEGER ::= 16 +ub-initials-length INTEGER ::= 5 +ub-integer-options INTEGER ::= 256 +ub-numeric-user-id-length INTEGER ::= 32 +ub-organization-name-length INTEGER ::= 64 +ub-organizational-unit-name-length INTEGER ::= 32 +ub-organizational-units INTEGER ::= 4 +ub-pds-name-length INTEGER ::= 16 +ub-pds-parameter-length INTEGER ::= 30 +ub-pds-physical-address-lines INTEGER ::= 6 +ub-postal-code-length INTEGER ::= 16 +ub-surname-length INTEGER ::= 40 +ub-terminal-id-length INTEGER ::= 24 +ub-unformatted-address-length INTEGER ::= 180 +ub-x121-address-length INTEGER ::= 16 + +-- Note - upper bounds on string types, such as TeletexString, are +-- measured in characters. Excepting PrintableString or IA5String, a +-- significantly greater number of octets will be required to hold +-- such a value. As a minimum, 16 octets, or twice the specified upper +-- bound, whichever is the larger, should be allowed for TeletexString. +-- For UTF8String or UniversalString at least four times the upper +-- bound should be allowed. + + + +-- END of PKIX1Implicit88 + + +-- BEGIN of RFC2630 + +-- Cryptographic Message Syntax + +pkcs-7-ContentInfo ::= SEQUENCE { + contentType pkcs-7-ContentType, + content [0] EXPLICIT ANY DEFINED BY contentType } + +pkcs-7-DigestInfo ::= SEQUENCE { + digestAlgorithm pkcs-7-DigestAlgorithmIdentifier, + digest pkcs-7-Digest +} + +pkcs-7-Digest ::= OCTET STRING + +pkcs-7-ContentType ::= OBJECT IDENTIFIER + +pkcs-7-SignedData ::= SEQUENCE { + version pkcs-7-CMSVersion, + digestAlgorithms pkcs-7-DigestAlgorithmIdentifiers, + encapContentInfo pkcs-7-EncapsulatedContentInfo, + certificates [0] IMPLICIT pkcs-7-CertificateSet OPTIONAL, + crls [1] IMPLICIT pkcs-7-CertificateRevocationLists OPTIONAL, + signerInfos pkcs-7-SignerInfos +} + +pkcs-7-CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) } + +pkcs-7-DigestAlgorithmIdentifiers ::= SET OF pkcs-7-DigestAlgorithmIdentifier + +pkcs-7-DigestAlgorithmIdentifier ::= AlgorithmIdentifier + +pkcs-7-EncapsulatedContentInfo ::= SEQUENCE { + eContentType pkcs-7-ContentType, + eContent [0] EXPLICIT OCTET STRING OPTIONAL } + +-- We don't use CertificateList here since we only want +-- to read the raw data. +pkcs-7-CertificateRevocationLists ::= SET OF ANY + +pkcs-7-CertificateChoices ::= CHOICE { +-- Although the paper uses Certificate type, we +-- don't use it since, we don't need to parse it. +-- We only need to read and store it. + certificate ANY +} + +pkcs-7-CertificateSet ::= SET OF pkcs-7-CertificateChoices + +pkcs-7-SignerInfos ::= SET OF ANY -- this is not correct but we don't use it + -- anyway + + +-- BEGIN of RFC2986 + +-- Certificate requests +pkcs-10-CertificationRequestInfo ::= SEQUENCE { + version INTEGER { v1(0) }, + subject Name, + subjectPKInfo SubjectPublicKeyInfo, + attributes [0] Attributes +} + +Attributes ::= SET OF Attribute + +pkcs-10-CertificationRequest ::= SEQUENCE { + certificationRequestInfo pkcs-10-CertificationRequestInfo, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING +} + +-- stuff from PKCS#9 + +pkcs-9-ub-challengePassword INTEGER ::= 255 + +pkcs-9-certTypes OBJECT IDENTIFIER ::= {pkcs-9 22} +pkcs-9-crlTypes OBJECT IDENTIFIER ::= {pkcs-9 23} + +pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7} + +pkcs-9-challengePassword ::= CHOICE { + printableString PrintableString (SIZE (1..pkcs-9-ub-challengePassword)), + utf8String UTF8String (SIZE (1..pkcs-9-ub-challengePassword)) } + +pkcs-9-at-localKeyId OBJECT IDENTIFIER ::= {pkcs-9 21} + +pkcs-9-localKeyId ::= OCTET STRING + +pkcs-9-at-friendlyName OBJECT IDENTIFIER ::= {pkcs-9 20} + +pkcs-9-friendlyName ::= BMPString (SIZE (1..255)) + +-- PKCS #8 stuff + +-- Private-key information syntax + +pkcs-8-PrivateKeyInfo ::= SEQUENCE { + version pkcs-8-Version, + privateKeyAlgorithm AlgorithmIdentifier, + privateKey pkcs-8-PrivateKey, + attributes [0] Attributes OPTIONAL } + +pkcs-8-Version ::= INTEGER {v1(0)} + +pkcs-8-PrivateKey ::= OCTET STRING + +pkcs-8-Attributes ::= SET OF Attribute + +-- Encrypted private-key information syntax + +pkcs-8-EncryptedPrivateKeyInfo ::= SEQUENCE { + encryptionAlgorithm AlgorithmIdentifier, + encryptedData pkcs-8-EncryptedData +} + +pkcs-8-EncryptedData ::= OCTET STRING + +-- PKCS #5 stuff + +pkcs-5 OBJECT IDENTIFIER ::= + { pkcs 5 } + +pkcs-5-encryptionAlgorithm OBJECT IDENTIFIER ::= + { iso(1) member-body(2) us(840) rsadsi(113549) 3 } + +pkcs-5-des-EDE3-CBC OBJECT IDENTIFIER ::= {pkcs-5-encryptionAlgorithm 7} + +pkcs-5-des-EDE3-CBC-params ::= OCTET STRING (SIZE(8)) + +pkcs-5-des-CBC-params ::= OCTET STRING (SIZE(8)) + +pkcs-5-rc2-CBC-params ::= SEQUENCE { + rc2ParameterVersion INTEGER OPTIONAL, + iv OCTET STRING (SIZE(8)) +} + +pkcs-5-PBE-params ::= SEQUENCE { + salt OCTET STRING (SIZE(8)), + iterationCount INTEGER +} + +pkcs-5-id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} + +pkcs-5-PBES2-params ::= SEQUENCE { + keyDerivationFunc AlgorithmIdentifier, + encryptionScheme AlgorithmIdentifier } + +-- PBKDF2 + +pkcs-5-id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} + +-- pkcs-5-id-hmacWithSHA1 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840) rsadsi(113549) 2 7} + +-- pkcs-5-algid-hmacWithSHA1 AlgorithmIdentifier ::= +-- {algorithm pkcs-5-id-hmacWithSHA1, parameters NULL : NULL} + +pkcs-5-PBKDF2-params ::= SEQUENCE { + salt CHOICE { + specified OCTET STRING, + otherSource AlgorithmIdentifier + }, + iterationCount INTEGER (1..MAX), + keyLength INTEGER (1..MAX) OPTIONAL, + prf AlgorithmIdentifier OPTIONAL -- DEFAULT pkcs-5-id-hmacWithSHA1 +} + +-- PKCS #12 stuff + +pkcs-12 OBJECT IDENTIFIER ::= {pkcs 12} + +pkcs-12-PFX ::= SEQUENCE { + version INTEGER {v3(3)}, + authSafe pkcs-7-ContentInfo, + macData pkcs-12-MacData OPTIONAL +} + +pkcs-12-PbeParams ::= SEQUENCE { + salt OCTET STRING, + iterations INTEGER +} + +pkcs-12-MacData ::= SEQUENCE { + mac pkcs-7-DigestInfo, + macSalt OCTET STRING, + iterations INTEGER DEFAULT 1 +-- Note: The default is for historical reasons and its use is +-- deprecated. A higher value, like 1024 is recommended. +} + +pkcs-12-AuthenticatedSafe ::= SEQUENCE OF pkcs-7-ContentInfo + -- Data if unencrypted + -- EncryptedData if password-encrypted + -- EnvelopedData if public key-encrypted + +pkcs-12-SafeContents ::= SEQUENCE OF pkcs-12-SafeBag + +pkcs-12-SafeBag ::= SEQUENCE { + bagId OBJECT IDENTIFIER, + bagValue [0] EXPLICIT ANY DEFINED BY badId, + bagAttributes SET OF pkcs-12-PKCS12Attribute OPTIONAL +} + +-- Bag types + + +pkcs-12-bagtypes OBJECT IDENTIFIER ::= {pkcs-12 10 1} + +pkcs-12-keyBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 1} +pkcs-12-pkcs8ShroudedKeyBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 2} +pkcs-12-certBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 3} +pkcs-12-crlBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 4} + +pkcs-12-KeyBag ::= pkcs-8-PrivateKeyInfo + +-- Shrouded KeyBag + +pkcs-12-PKCS8ShroudedKeyBag ::= pkcs-8-EncryptedPrivateKeyInfo + +-- CertBag + +pkcs-12-CertBag ::= SEQUENCE { + certId OBJECT IDENTIFIER, + certValue [0] EXPLICIT ANY DEFINED BY certId +} + +-- x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {pkcs-9-certTypes 1}} +-- DER-encoded X.509 certificate stored in OCTET STRING + +pkcs-12-CRLBag ::= SEQUENCE { + crlId OBJECT IDENTIFIER, + crlValue [0] EXPLICIT ANY DEFINED BY crlId +} + +-- x509CRL BAG-TYPE ::= +-- {OCTET STRING IDENTIFIED BY {pkcs-9-crlTypes 1}} +-- DER-encoded X.509 CRL stored in OCTET STRING + +pkcs-12-PKCS12Attribute ::= Attribute + +-- PKCS #7 stuff (needed in PKCS 12) + +pkcs-7-data OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 } + +pkcs-7-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 } + +pkcs-7-Data ::= OCTET STRING + +pkcs-7-EncryptedData ::= SEQUENCE { + version pkcs-7-CMSVersion, + encryptedContentInfo pkcs-7-EncryptedContentInfo, + unprotectedAttrs [1] IMPLICIT pkcs-7-UnprotectedAttributes OPTIONAL } + +pkcs-7-EncryptedContentInfo ::= SEQUENCE { + contentType pkcs-7-ContentType, + contentEncryptionAlgorithm pkcs-7-ContentEncryptionAlgorithmIdentifier, + encryptedContent [0] IMPLICIT pkcs-7-EncryptedContent OPTIONAL } + +pkcs-7-ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + +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 } + +ldap-DC ::= IA5String + +id-at-ldap-UID AttributeType ::= { 0 9 2342 19200300 100 1 1 } + +ldap-UID ::= DirectoryString + +-- rfc3039 + +id-pda OBJECT IDENTIFIER ::= { id-pkix 9 } + +id-pda-dateOfBirth AttributeType ::= { id-pda 1 } +DateOfBirth ::= GeneralizedTime + +id-pda-placeOfBirth AttributeType ::= { id-pda 2 } +PlaceOfBirth ::= DirectoryString + +id-pda-gender AttributeType ::= { id-pda 3 } +Gender ::= PrintableString (SIZE(1)) + -- "M", "F", "m" or "f" + +id-pda-countryOfCitizenship AttributeType ::= { id-pda 4 } +CountryOfCitizenship ::= PrintableString (SIZE (2)) + -- ISO 3166 Country Code + +id-pda-countryOfResidence AttributeType ::= { id-pda 5 } +CountryOfResidence ::= PrintableString (SIZE (2)) + -- ISO 3166 Country Code + +END diff --git a/egg/tests/Makefile.am b/egg/tests/Makefile.am new file mode 100644 index 0000000..b517ef5 --- /dev/null +++ b/egg/tests/Makefile.am @@ -0,0 +1,23 @@ + +BUILT_SOURCES = \ + asn1-def-test.h + +asn1-def-test.h: test.asn + asn1Parser -o asn1-def-test.h $(srcdir)/test.asn + +# Test files should be listed in order they need to run +UNIT_AUTO = \ + unit-test-asn1.c \ + unit-test-secmem.c \ + $(BUILT_SOURCES) + +UNIT_PROMPT = + +UNIT_LIBS = \ + $(top_builddir)/egg/libegg.la + +EXTRA_DIST = \ + test.asn \ + test-data + +include $(top_srcdir)/tests/gtest.make diff --git a/egg/tests/test.asn b/egg/tests/test.asn new file mode 100644 index 0000000..0fdf483 --- /dev/null +++ b/egg/tests/test.asn @@ -0,0 +1,19 @@ +TEST { } + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +TestIntegers ::= SEQUENCE { + uint1 INTEGER, + uint2 INTEGER, + uint3 INTEGER, + mpi INTEGER +} + +TestData ::= SEQUENCE { + data OCTET STRING, + boolean BOOLEAN DEFAULT FALSE +} + +END diff --git a/egg/tests/unit-test-asn1.c b/egg/tests/unit-test-asn1.c new file mode 100644 index 0000000..a2b84d2 --- /dev/null +++ b/egg/tests/unit-test-asn1.c @@ -0,0 +1,417 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* unit-test-pkix-parser.c: Test PKIX parser + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#include "config.h" + +#include "run-auto-test.h" + +#include "egg/egg-asn1.h" + +#include +#include +#include + +#include +#include +#include + +#define extern +#include "asn1-def-test.h" +#undef extern + +static ASN1_TYPE asn1_test = NULL; + +static ASN1_TYPE asn1_cert = NULL; +static guchar *data_cert = NULL; +static gsize n_data_cert = 0; + +DEFINE_SETUP(asn1_tree) +{ + ASN1_TYPE pkix; + + int res = asn1_array2tree (test_asn1_tab, &asn1_test, NULL); + g_assert (res == ASN1_SUCCESS); + + /* -------- */ + + data_cert = test_read_testdata ("test-certificate-1.der", &n_data_cert); + + /* We'll be catching this error later */ + pkix = egg_asn1_get_pkix_asn1type (); + if (!pkix) return; + + res = asn1_create_element (pkix, "PKIX1.Certificate", &asn1_cert); + g_assert (res == ASN1_SUCCESS); + + res = asn1_der_decoding (&asn1_cert, data_cert, n_data_cert, NULL); + g_assert (res == ASN1_SUCCESS); +} + +DEFINE_TEARDOWN(asn1_tree) +{ + asn1_delete_structure (&asn1_test); + asn1_delete_structure (&asn1_cert); + g_free (data_cert); + data_cert = NULL; +} + +DEFINE_TEST(asn1_types) +{ + ASN1_TYPE asn; + + asn = egg_asn1_get_pk_asn1type (); + g_assert ("pk asn type is null" && asn != NULL); + + asn = egg_asn1_get_pkix_asn1type (); + g_assert ("pkix asn type is null" && asn != NULL); +} + +DEFINE_TEST(asn1_integers) +{ + ASN1_TYPE asn; + guchar *data; + gsize n_data; + gboolean ret; + guint val; + int res; + + res = asn1_create_element (asn1_test, "TEST.TestIntegers", &asn); + g_assert ("asn test structure is null" && asn != NULL); + + ret = egg_asn1_write_uint (asn, "uint1", 35); + g_assert ("couldn't write integer" && ret); + + ret = egg_asn1_write_uint (asn, "uint2", 23456); + g_assert ("couldn't write integer" && ret); + + ret = egg_asn1_write_uint (asn, "uint3", 209384022); + g_assert ("couldn't write integer" && ret); + + /* Now encode the whole caboodle */ + data = egg_asn1_encode (asn, "", &n_data, NULL); + g_assert ("encoding asn1 didn't work" && data != NULL); + + asn1_delete_structure (&asn); + + /* Now decode it all nicely */ + res = asn1_create_element (asn1_test, "TEST.TestIntegers", &asn); + g_return_if_fail (res == ASN1_SUCCESS); + + res = asn1_der_decoding (&asn, data, n_data, NULL); + g_assert ("decoding asn didn't work" && res == ASN1_SUCCESS); + + /* And get out the values */ + ret = egg_asn1_read_uint (asn, "uint1", &val); + g_assert ("couldn't read integer from asn1" && ret); + g_assert_cmpuint (val, ==, 35); + + ret = egg_asn1_read_uint (asn, "uint2", &val); + g_assert ("couldn't read integer from asn1" && ret); + g_assert_cmpuint (val, ==, 23456); + + ret = egg_asn1_read_uint (asn, "uint3", &val); + g_assert ("couldn't read integer from asn1" && ret); + g_assert_cmpuint (val, ==, 209384022); +} + +DEFINE_TEST(boolean) +{ + ASN1_TYPE asn = NULL; + gboolean value, ret; + int res; + + res = asn1_create_element (asn1_test, "TEST.TestData", &asn); + g_assert ("asn test structure is null" && asn != NULL); + + res = asn1_write_value (asn, "boolean", "TRUE", 4); + g_assert (res == ASN1_SUCCESS); + + ret = egg_asn1_read_boolean (asn, "boolean", &value); + g_assert (ret); + g_assert (value == TRUE); + + res = asn1_write_value (asn, "boolean", "FALSE", 5); + g_assert (res == ASN1_SUCCESS); + + ret = egg_asn1_read_boolean (asn, "boolean", &value); + g_assert (ret); + g_assert (value == FALSE); + + ret = egg_asn1_read_boolean (asn, "nonExistant", &value); + g_assert (!ret); + + asn1_delete_structure (&asn); +} + +DEFINE_TEST(write_value) +{ + ASN1_TYPE asn = NULL; + guchar *data; + gsize n_data; + int res; + + res = asn1_create_element (asn1_test, "TEST.TestData", &asn); + g_assert ("asn test structure is null" && asn != NULL); + + if (!egg_asn1_write_value (asn, "data", (const guchar*)"SOME DATA", 9)) + g_assert_not_reached (); + + data = egg_asn1_read_value (asn, "data", &n_data, NULL); + g_assert (data != NULL); + g_assert_cmpuint (n_data, ==, 9); + g_assert (memcmp (data, "SOME DATA", 9) == 0); + g_free (data); + + asn1_delete_structure (&asn); +} + +DEFINE_TEST(element_length_content) +{ + ASN1_TYPE asn = NULL; + guchar buffer[1024]; + const guchar *content; + gsize n_content; + gint length; + int res; + + res = asn1_create_element (asn1_test, "TEST.TestData", &asn); + g_assert ("asn test structure is null" && asn != NULL); + + res = asn1_write_value (asn, "data", "SOME DATA", 9); + g_assert (res == ASN1_SUCCESS); + + length = 1024; + res = asn1_der_coding (asn, "", buffer, &length, NULL); + g_assert (res == ASN1_SUCCESS); + + /* Now the real test */ + length = egg_asn1_element_length (buffer, 1024); + g_assert_cmpint (length, ==, 13); + + content = egg_asn1_element_content (buffer, length, &n_content); + g_assert (content); + g_assert_cmpuint (n_content, ==, 11); + + content = egg_asn1_element_content (content, n_content, &n_content); + g_assert (content); + g_assert_cmpuint (n_content, ==, 9); + g_assert (memcmp (content, "SOME DATA", 9) == 0); + + asn1_delete_structure (&asn); +} + +DEFINE_TEST(read_element) +{ + ASN1_TYPE asn = NULL; + guchar buffer[1024]; + const guchar *data; + gsize n_data; + gint length; + int res; + + res = asn1_create_element (asn1_test, "TEST.TestData", &asn); + g_assert ("asn test structure is null" && asn != NULL); + + res = asn1_write_value (asn, "data", "SOME DATA", 9); + g_assert (res == ASN1_SUCCESS); + + length = 1024; + res = asn1_der_coding (asn, "", buffer, &length, NULL); + g_assert (res == ASN1_SUCCESS); + + /* Now the real test */ + data = egg_asn1_read_element (asn, buffer, length, "data", &n_data); + g_assert (data != NULL); + g_assert_cmpint (n_data, ==, 11); + + data = egg_asn1_read_content (asn, buffer, length, "data", &n_data); + g_assert (data); + g_assert_cmpuint (n_data, ==, 9); + g_assert (memcmp (data, "SOME DATA", 9) == 0); + + /* Invalid should return null for both those */ + data = egg_asn1_read_element (asn, buffer, length, "nonExistant", &n_data); + g_assert (data == NULL); + data = egg_asn1_read_content (asn, buffer, length, "nonExistant", &n_data); + g_assert (data == NULL); + + asn1_delete_structure (&asn); +} + +DEFINE_TEST(oid) +{ + ASN1_TYPE asn = NULL; + GQuark oid, check; + int res; + + res = asn1_create_element (asn1_test, "TEST.TestData", &asn); + g_assert ("asn test structure is null" && asn != NULL); + + res = asn1_write_value (asn, "data", "SOME DATA", 9); + g_assert (res == ASN1_SUCCESS); + + /* No such element, should return 0 */ + oid = egg_asn1_read_oid (asn, "nonExistant"); + g_assert (oid == 0); + + /* No quark of this has been defined, so should return an invalid OID */ + oid = egg_asn1_read_oid (asn, "data"); + g_assert (oid != 0); + g_assert_cmpstr (g_quark_to_string (oid), !=, "SOME DATA"); + + /* Now a quark has been defined */ + check = g_quark_from_static_string ("SOME DATA"); + oid = egg_asn1_read_oid (asn, "data"); + g_assert (check == oid); + g_assert_cmpstr (g_quark_to_string (oid), ==, "SOME DATA"); + + /* Write a different OID */ + if (!egg_asn1_write_oid (asn, "data", g_quark_from_static_string ("ANOTHER"))) + g_assert_not_reached (); + + oid = egg_asn1_read_oid (asn, "data"); + g_assert (oid); + g_assert_cmpstr (g_quark_to_string (oid), ==, "ANOTHER"); + + asn1_delete_structure (&asn); +} + +typedef struct _TimeTestData { + gchar *value; + time_t ref; +} TimeTestData; + +static const TimeTestData generalized_time_test_data[] = { + { "20070725130528Z", 1185368728 }, + { "20070725130528.2134Z", 1185368728 }, + { "20070725140528-0100", 1185368728 }, + { "20070725040528+0900", 1185368728 }, + { "20070725013528+1130", 1185368728 }, + { "20070725Z", 1185321600 }, + { "20070725+0000", 1185321600 }, + { NULL, 0 } +}; + +static const TimeTestData utc_time_test_data[] = { + /* Test the Y2K style wrap arounds */ + { "070725130528Z", 1185368728 }, /* The year 2007 */ + { "020725130528Z", 1027602328 }, /* The year 2002 */ + { "970725130528Z", 869835928 }, /* The year 1997 */ + { "370725130528Z", 2132139928 }, /* The year 2037 */ + + /* Test the time zones and other formats */ + { "070725130528.2134Z", 1185368728 }, + { "070725140528-0100", 1185368728 }, + { "070725040528+0900", 1185368728 }, + { "070725013528+1130", 1185368728 }, + { "070725Z", 1185321600 }, + { "070725+0000", 1185321600 }, + + { NULL, 0 } +}; + +DEFINE_TEST(general_time) +{ + time_t when; + const TimeTestData *data; + + for (data = generalized_time_test_data; data->value; ++data) { + when = egg_asn1_parse_general_time (data->value); + if (data->ref != when) { + printf ("%s", data->value); + printf ("%s != ", ctime (&when)); + printf ("%s\n", ctime (&data->ref)); + fflush (stdout); + } + + g_assert ("decoded time doesn't match reference" && data->ref == when); + } +} + +DEFINE_TEST(utc_time) +{ + time_t when; + const TimeTestData *data; + + for (data = utc_time_test_data; data->value; ++data) { + when = egg_asn1_parse_utc_time (data->value); + if (data->ref != when) { + printf ("%s", data->value); + printf ("%s != ", ctime (&when)); + printf ("%s\n", ctime (&data->ref)); + fflush (stdout); + } + + g_assert ("decoded time doesn't match reference" && data->ref == when); + } +} + +DEFINE_TEST(read_time) +{ + time_t time; + + if (!egg_asn1_read_time (asn1_cert, "tbsCertificate.validity.notBefore", &time)) + g_assert_not_reached (); + g_assert_cmpint (time, ==, 820454400); +} + +DEFINE_TEST(read_dn) +{ + gchar *dn; + + dn = egg_asn1_read_dn (asn1_cert, "tbsCertificate.issuer.rdnSequence"); + g_assert (dn != NULL); + g_assert_cmpstr (dn, ==, "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_free (dn); + + dn = egg_asn1_read_dn (asn1_cert, "tbsCertificate.nonExistant"); + g_assert (dn == NULL); +} + +DEFINE_TEST(read_dn_part) +{ + gchar *value; + + value = egg_asn1_read_dn_part (asn1_cert, "tbsCertificate.issuer.rdnSequence", "CN"); + g_assert (value != NULL); + g_assert_cmpstr (value, ==, "Thawte Personal Premium CA"); + g_free (value); + + value = egg_asn1_read_dn_part (asn1_cert, "tbsCertificate.issuer.rdnSequence", "2.5.4.8"); + g_assert (value != NULL); + g_assert_cmpstr (value, ==, "Western Cape"); + g_free (value); + + value = egg_asn1_read_dn_part (asn1_cert, "tbsCertificate.nonExistant", "CN"); + g_assert (value == NULL); + + value = egg_asn1_read_dn_part (asn1_cert, "tbsCertificate.issuer.rdnSequence", "DC"); + g_assert (value == NULL); + + value = egg_asn1_read_dn_part (asn1_cert, "tbsCertificate.issuer.rdnSequence", "0.0.0.0"); + g_assert (value == NULL); + + value = egg_asn1_read_dn_part (asn1_cert, "tbsCertificate.issuer.rdnSequence", "2.5.4.9"); + g_assert (value == NULL); +} diff --git a/egg/tests/unit-test-secmem.c b/egg/tests/unit-test-secmem.c new file mode 100644 index 0000000..a75b897 --- /dev/null +++ b/egg/tests/unit-test-secmem.c @@ -0,0 +1,139 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* unit-test-secmem.c: Test low level secure memory allocation functionality + + Copyright (C) 2007 Stefan Walter + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#include +#include +#include + +#include "run-auto-test.h" + +#include "egg/egg-secure-memory.h" + +/* + * Each test looks like (on one line): + * void unit_test_xxxxx (CuTest* cu) + * + * Each setup looks like (on one line): + * void unit_setup_xxxxx (void); + * + * Each teardown looks like (on one line): + * void unit_teardown_xxxxx (void); + * + * Tests be run in the order specified here. + */ + +#define IS_ZERO ~0 + +static gsize +find_non_zero (gpointer mem, gsize len) +{ + guchar *b, *e; + gsize sz = 0; + for (b = (guchar*)mem, e = ((guchar*)mem) + len; b != e; ++b, ++sz) { + if (*b != 0x00) + return sz; + } + + return IS_ZERO; +} + +DEFINE_TEST(secmem_alloc_free) +{ + gpointer p; + gboolean ret; + + p = egg_secure_alloc_full (512, 0); + g_assert (p != NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (p, 512)); + + memset (p, 0x67, 512); + + ret = egg_secure_check (p); + g_assert (ret == TRUE); + + egg_secure_free_full (p, 0); +} + +DEFINE_TEST(secmem_realloc_across) +{ + gpointer p, p2; + + /* Tiny allocation */ + p = egg_secure_realloc_full (NULL, 1088, 0); + g_assert (p != NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (p, 1088)); + + /* Reallocate to a large one, will have to have changed blocks */ + p2 = egg_secure_realloc_full (p, 16200, 0); + g_assert (p2 != NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (p2, 16200)); +} + +DEFINE_TEST(secmem_alloc_two) +{ + gpointer p, p2; + gboolean ret; + + p2 = egg_secure_alloc_full (4, 0); + g_assert (p2 != NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (p2, 4)); + + memset (p2, 0x67, 4); + + p = egg_secure_alloc_full (16200, 0); + g_assert (p != NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (p, 16200)); + + memset (p, 0x67, 16200); + + ret = egg_secure_check (p); + g_assert (ret == TRUE); + + egg_secure_free_full (p2, 0); + egg_secure_free_full (p, 0); +} + +DEFINE_TEST(secmem_realloc) +{ + gchar *str = "a test string to see if realloc works properly"; + gpointer p, p2; + gsize len; + + len = strlen (str) + 1; + + p = egg_secure_realloc_full (NULL, len, 0); + g_assert (p != NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (p, len)); + + strcpy ((gchar*)p, str); + + p2 = egg_secure_realloc_full (p, 512, 0); + g_assert (p2 == NULL); + g_assert_cmpint (IS_ZERO, ==, find_non_zero (((gchar*)p2) + len, 512 - len)); + + g_assert (strcmp (p2, str) == 0); + + p = egg_secure_realloc_full (p2, 0, 0); + g_assert (p == NULL); +} +