Consolidate truly common functionality into 'egg' library. Many more files
authorStefan Walter <stefw@src.gnome.org>
Sat, 17 Jan 2009 23:27:10 +0000 (23:27 +0000)
committerStefan Walter <stefw@src.gnome.org>
Sat, 17 Jan 2009 23:27:10 +0000 (23:27 +0000)
* 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

13 files changed:
egg/Makefile.am [new file with mode: 0644]
egg/egg-asn1.c [new file with mode: 0644]
egg/egg-asn1.h [new file with mode: 0644]
egg/egg-buffer.c [new file with mode: 0644]
egg/egg-buffer.h [new file with mode: 0644]
egg/egg-secure-memory.c [new file with mode: 0644]
egg/egg-secure-memory.h [new file with mode: 0644]
egg/pk.asn [new file with mode: 0644]
egg/pkix.asn [new file with mode: 0644]
egg/tests/Makefile.am [new file with mode: 0644]
egg/tests/test.asn [new file with mode: 0644]
egg/tests/unit-test-asn1.c [new file with mode: 0644]
egg/tests/unit-test-secmem.c [new file with mode: 0644]

diff --git a/egg/Makefile.am b/egg/Makefile.am
new file mode 100644 (file)
index 0000000..a548c96
--- /dev/null
@@ -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 (file)
index 0000000..a127d50
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+#include "config.h"
+
+#include "egg-asn1.h"
+
+#include <libtasn1.h>
+
+#include <string.h>
+
+/*
+ * 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 (file)
index 0000000..4e74f9d
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+#ifndef EGG_ASN1_H_
+#define EGG_ASN1_H_
+
+#include <glib.h>
+
+#include <libtasn1.h>
+
+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 (file)
index 0000000..bbda991
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+#include "config.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..c937460
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+#ifndef EGG_BUFFER_H
+#define EGG_BUFFER_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/* -------------------------------------------------------------------
+ * 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 (file)
index 0000000..39f6bf7
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+/*
+ * 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 <sys/types.h>
+#include <sys/mman.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+/*
+ * 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 <mba2000 ioplex.com>
+ *
+ * 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 (file)
index 0000000..f4ddf3c
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+#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 (file)
index 0000000..f347c19
--- /dev/null
@@ -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 (file)
index 0000000..1e5d657
--- /dev/null
@@ -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 (file)
index 0000000..b517ef5
--- /dev/null
@@ -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 (file)
index 0000000..0fdf483
--- /dev/null
@@ -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 (file)
index 0000000..a2b84d2
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+#include "config.h"
+
+#include "run-auto-test.h"
+
+#include "egg/egg-asn1.h"
+
+#include <glib.h>
+#include <gcrypt.h>
+#include <libtasn1.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 (file)
index 0000000..a75b897
--- /dev/null
@@ -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 <stef@memberwebs.com>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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);
+}
+