[gnome-keyring/asn1-work: 2/18] [egg] Initial asn1 parser work.



commit 43ff1c1f495a1df0348a736ed76871ed91533d53
Author: Stef Walter <stef memberwebs com>
Date:   Mon Dec 21 17:38:14 2009 +0000

    [egg] Initial asn1 parser work.

 egg/.gitignore         |    2 +
 egg/Makefile.am        |   10 +-
 egg/egg-asn1x.c        |  617 ++++++++++++++++++++++++++++++++++++++++++++++++
 egg/egg-asn1x.h        |   52 ++++
 egg/tests/Makefile.am  |   12 +
 egg/tests/test-asn1x.c |   30 +++
 6 files changed, 722 insertions(+), 1 deletions(-)
---
diff --git a/egg/.gitignore b/egg/.gitignore
index 6363a8b..1142560 100644
--- a/egg/.gitignore
+++ b/egg/.gitignore
@@ -1,2 +1,4 @@
 /asn1-def-pk.h
 /asn1-def-pkix.h
+/tests/test-asn1x
+
diff --git a/egg/Makefile.am b/egg/Makefile.am
index 7c620b0..0fc9654 100644
--- a/egg/Makefile.am
+++ b/egg/Makefile.am
@@ -1,6 +1,7 @@
 
 noinst_LTLIBRARIES = \
 	libegg.la \
+	libegg-asn1x.la \
 	libegg-buffer.la \
 	libegg-creds.la \
 	libegg-dbus.la \
@@ -52,7 +53,14 @@ DISTCLEANFILES = \
 	
 # --------------------------------------------------------------------
 # COMMON STUFF COMPILED INTO SMALLER COMPONENTS
- 
+
+libegg_asn1x_la_SOURCES = \
+	egg-asn1x.c egg-asn1x.h
+
+libegg_asn1x_la_CFLAGS = \
+	$(LIBTASN1_CFLAGS) \
+	$(GLIB_CFLAGS)
+
 libegg_secure_la_SOURCES = \
 	egg-secure-memory.c egg-secure-memory.h
 
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
new file mode 100644
index 0000000..8c29388
--- /dev/null
+++ b/egg/egg-asn1x.c
@@ -0,0 +1,617 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-asn1x.c - ASN.1/DER parse and coding routines
+
+   Copyright (C) 2009 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-asn1x.h"
+
+#include <libtasn1.h>
+
+#include <string.h>
+
+enum {
+	NO_VALUE = 0,
+	DER_VALUE,
+	C_VALUE
+};
+
+/* From libtasn1's int.h */
+enum {
+	TYPE_CONSTANT = 1,
+	TYPE_IDENTIFIER = 2,
+	TYPE_INTEGER = 3,
+	TYPE_BOOLEAN = 4,
+	TYPE_SEQUENCE = 5,
+	TYPE_BIT_STRING = 6,
+	TYPE_OCTET_STRING = 7,
+	TYPE_TAG = 8,
+	TYPE_DEFAULT = 9,
+	TYPE_SIZE = 10,
+	TYPE_SEQUENCE_OF = 11,
+	TYPE_OBJECT_ID = 12,
+	TYPE_ANY = 13,
+	TYPE_SET = 14,
+	TYPE_SET_OF = 15,
+	TYPE_DEFINITIONS = 16,
+	TYPE_TIME = 17,
+	TYPE_CHOICE = 18,
+	TYPE_IMPORTS = 19,
+	TYPE_NULL = 20,
+	TYPE_ENUMERATED = 21,
+	TYPE_GENERALSTRING = 27
+};
+
+enum {
+	CONST_UNIVERSAL = (1<<8),
+	CONST_PRIVATE = (1<<9),
+	CONST_APPLICATION = (1<<10),
+	CONST_EXPLICIT = (1<<11),
+	CONST_IMPLICIT = (1<<12),
+	CONST_TAG = (1<<13),
+	CONST_OPTION = (1<<14),
+	CONST_DEFAULT = (1<<15),
+	CONST_TRUE = (1<<16),
+	CONST_FALSE = (1<<17),
+	CONST_LIST = (1<<18),
+	CONST_MIN_MAX = (1<<19),
+	CONST_1_PARAM = (1<<20),
+	CONST_SIZE = (1<<21),
+	CONST_DEFINED_BY = (1<<22),
+	CONST_GENERALIZED = (1<<23),
+	CONST_UTC = (1<<24),
+	CONST_IMPORTS = (1<<25),
+	CONST_NOT_USED = (1<<26),
+	CONST_SET = (1<<27),
+	CONST_ASSIGN = (1<<28),
+	CONST_DOWN = (1<<29),
+	CONST_RIGHT = (1<<30),
+};
+
+typedef struct Anode {
+	const gchar *name;
+	guint type;
+	const gchar *value;
+	gint state;
+	gconstpointer data;
+	gsize n_data;
+} Anode;
+
+/* Forward Declarations */
+static gssize anode_decode_any (GNode*, const guchar*, gsize);
+
+static Anode*
+anode_new (const ASN1_ARRAY_TYPE *def)
+{
+	Anode *an = g_slice_new0 (Anode);
+	an->name = def->name;
+	an->value = def->value;
+	an->type = def->type;
+	an->state = NO_VALUE;
+	an->data = NULL;
+	an->n_data = 0;
+	return an;
+}
+
+static void
+anode_free (gpointer data)
+{
+	if (data)
+		g_slice_free (Anode, data);
+}
+
+#if 0
+static Anode*
+anode_decode_until (GNode **node, int type)
+{
+	Anode *an;
+
+	g_assert (*node);
+	while (*node) {
+		an = (*node)->data;
+		if ((an->type & 0xFF) == type)
+			return an;
+
+		/* The node must be optional or have a default */
+		g_assert_not_reached (); /* TODO: */
+		*node = (*node)->next;
+	}
+
+	return NULL;
+}
+
+
+
+
+static GNode*
+anode_decode_utc_time (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	if (n_data < 6 || n_data >= 28)
+		return NULL;
+	an->data = data;
+	an->n_data = n_data;
+	return node;
+}
+
+static GNode*
+anode_decode_generalized_time (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	if (n_data < 8 || n_data >= 30)
+		return NULL;
+	an->data = data;
+	an->n_data = n_data;
+	return node;
+}
+
+static GNode*
+anode_decode_sequence (GNode *node, const guchar *data, gsize n_data)
+{
+
+}
+
+static gssize
+anode_decode_any (GNode *node, const guchar *data, gsize n_data)
+{
+	guchar cls;
+	gulong tag;
+	gint cb, len;
+	gsize offset = 0;
+	gsize ret;
+
+	g_assert (node);
+
+	if (asn1_get_tag_der (data, n_data, &cls, &cb, &tag) != ASN1_SUCCESS)
+		return -1;
+
+	offset += cb;
+	data += cb;
+	n_data -= cb;
+
+	len = asn1_get_length_der (data + cb, n_data - cb, &cb);
+	if (len < 0)
+		return -1;
+
+	offset += cb;
+	data += cb;
+	n_data -= cb;
+
+	switch (tag) {
+	case ASN1_TAG_BOOLEAN:
+		ret = anode_decode_boolean (node, data, n_data);
+		break;
+	case ASN1_TAG_INTEGER:
+		ret = anode_decode_value (node, data, n_data);
+		break;
+	case ASN1_TAG_SEQUENCE:
+		ret = anode_decode_sequence (node, data, n_data);
+		break;
+	case ASN1_TAG_SET:
+		ret = anode_decode_set (node, data, n_data);
+		break;
+	case ASN1_TAG_OCTET_STRING:
+		ret = anode_decode_value (node, data, n_data);
+		break;
+	case ASN1_TAG_BIT_STRING:
+		ret = anode_decode_bit_string (node, data, n_data);
+		break;
+	case ASN1_TAG_UTCTime:
+		ret = anode_decode_utc_time (node, data, n_data);
+		break;
+	case ASN1_TAG_GENERALIZEDTime:
+		ret = anode_decode_generalized_time (node, data, n_data);
+		break;
+	case ASN1_TAG_OBJECT_ID:
+		ret = anode_decode_value (node, data, n_data);
+		break;
+	case ASN1_TAG_ENUMERATED:
+		ret = anode_decode_enumerated (node, data, n_data);
+		break;
+	case ASN1_TAG_NULL:
+		ret = anode_decode_null (node, data, n_data);
+		break;
+	case ASN1_TAG_GENERALSTRING:
+		ret = anode_decode_value (node, data, n_data);
+		break;
+	default:
+		g_return_val_if_reached (-1);
+	}
+
+	g_printerr ("class: %u(%x) / len: %d / tag: %lu\n", (guint)cls, (guint)(cls & 0x1F), len, tag);
+	return offset + len;
+}
+#endif
+
+static gsize
+anode_decode_tag (int ctag, int ccls, const guchar *data, gsize n_data)
+{
+	guchar cls;
+	gulong tag;
+	gint cb, len;
+	gsize offset = 0;
+
+	if (asn1_get_tag_der (data, n_data, &cls, &cb, &tag) != ASN1_SUCCESS)
+		return 0;
+	if (cls != ccls || tag != ctag)
+		return 0;
+
+	offset += cb;
+	data += cb;
+	n_data -= cb;
+
+	len = asn1_get_length_der (data, n_data , &cb);
+	if (len < 0)
+		return 0;
+
+	offset += cb;
+	n_data -= cb;
+	if (len != n_data)
+		return 0;
+
+	return offset;
+}
+
+static gsize
+anode_decode_length (const guchar *data, gsize n_data)
+{
+	guchar cls;
+	gulong tag;
+	gint cb1, cb2, len;
+
+	if (asn1_get_tag_der (data, n_data, &cls, &cb1, &tag) != ASN1_SUCCESS)
+		return -1;
+	len = asn1_get_length_der (data + cb1, n_data - cb1, &cb2);
+	if (len < 0)
+		return -1;
+	return len + cb1 + cb2;
+}
+
+static gboolean
+anode_decode_boolean (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset;
+
+	offset = anode_decode_tag (ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	data += offset;
+	n_data -= offset;
+	if (n_data != 1)
+		return FALSE;
+	if (data[0] != 1 && data[0] != 0)
+		return FALSE;
+	an->data = data;
+	an->n_data = 1;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_integer (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset;
+
+	offset = anode_decode_tag (ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	an->data = data + offset;
+	an->n_data = n_data - offset;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_bit_string (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset = anode_decode_tag (ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	data += offset;
+	n_data -= offset;
+	if (n_data < 2)
+		return FALSE;
+	if (data[0] < 8)
+		return FALSE;
+	an->data = data;
+	an->n_data = n_data;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_null (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset = anode_decode_tag (ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	data += offset;
+	n_data -= offset;
+	if (n_data - offset != 0)
+		return FALSE;
+	an->data = data + offset;
+	an->n_data = 0;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_octet_string (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset = anode_decode_tag (ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	an->data = data + offset;
+	an->n_data = n_data - offset;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_time (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset = anode_decode_tag (ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		offset = anode_decode_tag (ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	/* TODO: More validation */
+	an->data = data + offset;
+	an->n_data = n_data - offset;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_generalstring (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gsize offset = anode_decode_tag (ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, data, n_data);
+	if (!offset)
+		return FALSE;
+	/* TODO: More validation */
+	an->data = data + offset;
+	an->n_data = n_data - offset;
+	return TRUE;
+}
+
+static gboolean
+anode_decode_sequence (GNode *node, const guchar *data, gsize n_data)
+{
+	GNode *child;
+	gssize length;
+	gsize offset;
+
+	offset  = anode_decode_tag (ASN1_TAG_SEQUENCE, ASN1_CLASS_STRUCTURED, data, n_data);
+	if (!offset)
+		return FALSE;
+	data += offset;
+	n_data -= offset;
+
+	for (child = node->children; child; child = child->next) {
+		length = anode_decode_length (data, n_data);
+		if (length < 0)
+			return FALSE;
+		g_assert (length <= n_data);
+		if (!anode_decode_any (child, data, length))
+			return FALSE;
+		data += length;
+		n_data -= length;
+	}
+
+	return TRUE;
+}
+
+static gssize
+anode_decode_any (GNode *node, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	gboolean ret = FALSE;
+
+	switch (an->type & 0xFF) {
+	case TYPE_INTEGER:
+		ret = anode_decode_integer (node, data, n_data);
+		break;
+	case TYPE_BOOLEAN:
+		ret = anode_decode_boolean (node, data, n_data);
+		break;
+	case TYPE_BIT_STRING:
+		ret = anode_decode_bit_string (node, data, n_data);
+		break;
+	case TYPE_OCTET_STRING:
+		ret = anode_decode_octet_string (node, data, n_data);
+		break;
+	case TYPE_TIME:
+		ret = anode_decode_time (node, data, n_data);
+		break;
+	case TYPE_NULL:
+		ret = anode_decode_null (node, data, n_data);
+		break;
+	case TYPE_GENERALSTRING:
+		ret = anode_decode_generalstring (node, data, n_data);
+		break;
+	case TYPE_SEQUENCE:
+		ret = anode_decode_sequence (node, data, n_data);
+		break;
+
+	case TYPE_CONSTANT:
+	case TYPE_IDENTIFIER:
+	case TYPE_SEQUENCE_OF:
+	case TYPE_TAG:
+	case TYPE_DEFAULT:
+	case TYPE_SIZE:
+	case TYPE_OBJECT_ID:
+	case TYPE_ANY:
+	case TYPE_SET:
+	case TYPE_SET_OF:
+	case TYPE_DEFINITIONS:
+	case TYPE_CHOICE:
+	case TYPE_IMPORTS:
+	case TYPE_ENUMERATED:
+		g_assert_not_reached (); /* TODO: */
+		break;
+	}
+
+	if (!ret) {
+		/* Try to parse a context specific tag thingy */
+
+		g_assert_not_reached (); /* TODO: Implement checking */
+	}
+
+	return ret;
+}
+
+gboolean
+egg_asn1x_decode (GNode *asn, gconstpointer data, gsize n_data)
+{
+	g_return_val_if_fail (asn, FALSE);
+	g_return_val_if_fail (data, FALSE);
+	g_return_val_if_fail (n_data, FALSE);
+
+	return anode_decode_any (asn, data, n_data);
+}
+
+static void
+move_each_child (GNode *child, gpointer data)
+{
+	GNode *node = data;
+	g_node_unlink (child);
+	g_node_append (node, child);
+}
+
+static gboolean
+traverse_and_create_identifier (GNode *node, gpointer data)
+{
+	const ASN1_ARRAY_TYPE *defs = data;
+	Anode *an = node->data;
+	Anode *ans;
+	GNode *seq;
+
+	if ((an->type & 0xFF) == TYPE_IDENTIFIER) {
+		seq = egg_asn1x_create (defs, an->value);
+		g_return_val_if_fail (seq, TRUE);
+		ans = seq->data;
+		an->type = ans->type;
+		g_node_children_foreach (seq, G_TRAVERSE_ALL, move_each_child, node);
+	}
+
+	return FALSE;
+}
+
+GNode*
+egg_asn1x_create (const ASN1_ARRAY_TYPE *defs, const gchar *identifier)
+{
+	const ASN1_ARRAY_TYPE *def;
+	GNode *root, *parent, *node;
+	Anode *an;
+
+	g_return_val_if_fail (defs, NULL);
+	g_return_val_if_fail (identifier, NULL);
+
+	def = defs;
+
+	/* Find the one we're interested in */
+	while (def && (def->value || def->type || def->name)) {
+		if (def->name && g_str_equal (identifier, def->name))
+			break;
+		++def;
+	}
+
+	if (!def->name || !def->type)
+		return NULL;
+
+	/* The node for this item */
+	an = anode_new (def);
+	root = g_node_new (an);
+
+	/* Build up nodes for underlying level */
+	if (def->type & CONST_DOWN) {
+		node = root;
+		for (;;) {
+			if (def->type & CONST_DOWN) {
+				parent = node;
+			} else if (def->type & CONST_RIGHT) {
+				g_assert (node->parent);
+				parent = node->parent;
+			} else {
+				parent = node->parent;
+				while (parent) {
+					an = parent->data;
+					parent = parent->parent;
+					if (an->type & CONST_RIGHT)
+						break;
+				}
+			}
+
+			if (!parent)
+				break;
+
+			++def;
+			node = g_node_new (anode_new (def));
+			g_node_append (parent, node);
+		}
+	}
+
+	/* Load up sub identifiers */
+	g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+	                 traverse_and_create_identifier, (gpointer)defs);
+
+	return root;
+}
+
+static gboolean
+traverse_and_dump (GNode *node, gpointer data)
+{
+	guint i, depth;
+	Anode *an;
+	depth = g_node_depth (node);
+	for (i = 0; i < depth - 1; ++i)
+		g_printerr ("    ");
+	an = node->data;
+	g_printerr ("%s: %s [%08x]\n", an->name, an->value, (guint)an->type);
+	return FALSE;
+}
+
+void
+egg_asn1x_dump (GNode *asn)
+{
+	guint depth = 0;
+	g_return_if_fail (asn);
+	g_node_traverse (asn, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse_and_dump, &depth);
+}
+
+static gboolean
+traverse_and_free (GNode *node, gpointer data)
+{
+	anode_free (node->data);
+	return FALSE;
+}
+
+void
+egg_asn1x_destroy (gpointer data)
+{
+	GNode *asn = data;
+	if (!data)
+		return;
+	g_node_traverse (asn, G_IN_ORDER, G_TRAVERSE_ALL, -1, traverse_and_free, NULL);
+	g_node_destroy (asn);
+}
diff --git a/egg/egg-asn1x.h b/egg/egg-asn1x.h
new file mode 100644
index 0000000..94acb55
--- /dev/null
+++ b/egg/egg-asn1x.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-asn1.h - ASN.1/DER parsing and encoding routines
+
+   Copyright (C) 2009 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_ASN1X_H_
+#define EGG_ASN1X_H_
+
+#include <glib.h>
+
+#include <libtasn1.h>
+
+
+#ifndef HAVE_EGG_ALLOCATOR
+typedef void* (*EggAllocator) (void* p, gsize);
+#define HAVE_EGG_ALLOCATOR
+#endif
+
+GNode*              egg_asn1x_create             (const ASN1_ARRAY_TYPE *defs,
+                                                  const gchar *type);
+
+void                egg_asn1x_dump               (GNode *asn);
+
+gboolean            egg_asn1x_decode             (GNode *asn,
+                                                  gconstpointer data,
+                                                  gsize n_data);
+
+gpointer            egg_asn1x_encode             (GNode *asn,
+                                                  EggAllocator allocator,
+                                                  gsize *n_data);
+
+void                egg_asn1x_destroy            (gpointer asn);
+
+#endif /*EGG_ASN1X_H_*/
diff --git a/egg/tests/Makefile.am b/egg/tests/Makefile.am
index daae728..e8d3b86 100644
--- a/egg/tests/Makefile.am
+++ b/egg/tests/Makefile.am
@@ -28,3 +28,15 @@ include $(top_srcdir)/testing/testing.make
 
 BUILT_SOURCES += \
 	asn1-def-test.h
+
+# ------------------------------------------------------------------------------
+
+noinst_PROGRAMS += \
+	test-asn1x
+
+test_asn1x_SOURCES = \
+	test-asn1x.c
+
+test_asn1x_LDADD = \
+	$(top_builddir)/egg/libegg-asn1x.la \
+	$(LIBTASN1_LIBS)
diff --git a/egg/tests/test-asn1x.c b/egg/tests/test-asn1x.c
new file mode 100644
index 0000000..906c065
--- /dev/null
+++ b/egg/tests/test-asn1x.c
@@ -0,0 +1,30 @@
+
+#include "egg/egg-asn1x.h"
+#include "testing/testing.h"
+
+#include <pwd.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define extern
+#include "egg/asn1-def-pkix.h"
+#undef extern
+
+static int
+run (void)
+{
+	GNode *asn;
+	gpointer data;
+	gsize n_data;
+
+	data = testing_data_read ("test-certificate-1.der", &n_data);
+
+	asn = egg_asn1x_create (pkix_asn1_tab, "Certificate");
+	egg_asn1x_dump (asn);
+	if (!egg_asn1x_decode (asn, data, n_data))
+		g_assert_not_reached ();
+	egg_asn1x_destroy (asn);
+	return 0;
+}
+
+#include "testing/testing.c"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]