[gnome-keyring/asn1-work: 10/18] [egg] Rework asn1 decoding, add set support, much more robust.



commit 69e835a7ad4996f18b6f0c5b02fc3623a12e376f
Author: Stef Walter <stef memberwebs com>
Date:   Thu Dec 24 20:25:23 2009 +0000

    [egg] Rework asn1 decoding, add set support, much more robust.

 egg/egg-asn1x.c                             |  913 ++++++++++++++-------------
 egg/tests/test-asn1x.c                      |   57 ++-
 egg/tests/test-data/test-personalname-1.der |    1 +
 3 files changed, 527 insertions(+), 444 deletions(-)
---
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
index a03b977..4cde798 100644
--- a/egg/egg-asn1x.c
+++ b/egg/egg-asn1x.c
@@ -88,22 +88,27 @@ enum {
 	FLAG_RIGHT = (1<<30),
 };
 
-typedef struct _Adata {
-	gconstpointer buf;
-	gsize n_buf;
-	struct _Adata *next;
-} Adata;
+typedef struct _Atlv {
+	guchar cls;
+	gulong tag;
+	gint off;
+	gint len;
+	const guchar *buf;
+	const guchar *end;
+	struct _Atlv *next;
+} Atlv;
 
 typedef struct _Anode {
 	const ASN1_ARRAY_TYPE *def;
 	const ASN1_ARRAY_TYPE *join;
-	Adata *data;
+	Atlv *data;
 } Anode;
 
 /* TODO: Validate: LIST SIZE */
 
 /* Forward Declarations */
-static gssize anode_decode_anything (GNode*, const guchar*, gsize);
+static gboolean anode_decode_anything (GNode*, Atlv*);
+static gboolean anode_decode_anything_for_flags (GNode *, Atlv*, gint);
 
 static GNode*
 anode_new (const ASN1_ARRAY_TYPE *def)
@@ -119,7 +124,7 @@ anode_free_func (GNode *node, gpointer unused)
 {
 	Anode *an = node->data;
 	if (an->data);
-		g_slice_free_chain (Adata, an->data, next);
+		g_slice_free_chain (Atlv, an->data, next);
 	g_slice_free (Anode, an);
 	return FALSE;
 }
@@ -156,6 +161,39 @@ anode_def_type (GNode *node)
 	return type & 0xFF;
 }
 
+static gboolean
+anode_def_type_is_real (GNode *node)
+{
+	switch (anode_def_type (node)) {
+	case TYPE_INTEGER:
+	case TYPE_BOOLEAN:
+	case TYPE_SEQUENCE:
+	case TYPE_BIT_STRING:
+	case TYPE_OCTET_STRING:
+	case TYPE_SEQUENCE_OF:
+	case TYPE_OBJECT_ID:
+	case TYPE_ANY:
+	case TYPE_SET:
+	case TYPE_SET_OF:
+	case TYPE_TIME:
+	case TYPE_CHOICE:
+	case TYPE_NULL:
+	case TYPE_ENUMERATED:
+	case TYPE_GENERALSTRING:
+		return TRUE;
+	case TYPE_CONSTANT:
+	case TYPE_IDENTIFIER:
+	case TYPE_TAG:
+	case TYPE_DEFAULT:
+	case TYPE_SIZE:
+	case TYPE_DEFINITIONS:
+	case TYPE_IMPORTS:
+		return FALSE;
+	default:
+		g_return_val_if_reached (FALSE);
+	}
+}
+
 static int
 anode_def_flags (GNode *node)
 {
@@ -208,573 +246,553 @@ anode_child_with_type (GNode *node, gint type)
 }
 
 static GNode*
-anode_child_with_any_data_type (GNode *node)
+anode_child_with_real_type (GNode *node)
 {
 	GNode *child;
 
 	for (child = node->children; child; child = child->next) {
-		switch (anode_def_type (child)) {
-		case TYPE_INTEGER:
-		case TYPE_BOOLEAN:
-		case TYPE_SEQUENCE:
-		case TYPE_BIT_STRING:
-		case TYPE_OCTET_STRING:
-		case TYPE_SEQUENCE_OF:
-		case TYPE_OBJECT_ID:
-		case TYPE_ANY:
-		case TYPE_SET:
-		case TYPE_SET_OF:
-		case TYPE_TIME:
-		case TYPE_CHOICE:
-		case TYPE_NULL:
-		case TYPE_ENUMERATED:
-		case TYPE_GENERALSTRING:
+		if (anode_def_type_is_real (child))
 			return child;
-		case TYPE_CONSTANT:
-		case TYPE_IDENTIFIER:
-		case TYPE_TAG:
-		case TYPE_DEFAULT:
-		case TYPE_SIZE:
-		case TYPE_DEFINITIONS:
-		case TYPE_IMPORTS:
-			break;
-		default:
-			g_return_val_if_reached (NULL);
-		}
 	}
 
 	return NULL;
 }
 
-static gssize
-anode_decode_cls_tag_len (const guchar *data, gsize n_data,
-                          guchar *cls, gulong *tag, gint *len)
+static GNode*
+anode_next_with_real_type (GNode *node)
+{
+	for (node = node->next; node; node = node->next) {
+		if (anode_def_type_is_real (node))
+			return node;
+	}
+
+	return NULL;
+}
+
+static void
+anode_add_tlv_data (GNode *node, Atlv *tlv)
+{
+	Anode *an = node->data;
+	Atlv **last = &an->data;
+	g_assert (tlv->len >= 0);
+	while (*last)
+		last = &(*last)->next;
+	*last = g_slice_new0 (Atlv);
+	memcpy (*last, tlv, sizeof (tlv));
+}
+
+static gulong
+anode_encode_tag_for_flags (GNode *node, gint flags)
+{
+	GNode *child;
+
+	g_return_val_if_fail (anode_def_type_is_real (node), G_MAXULONG);
+
+	/* A context specific tag */
+	if (flags & FLAG_TAG) {
+		child = anode_child_with_type (node, TYPE_TAG);
+		g_return_val_if_fail (child, G_MAXULONG);
+		return anode_def_value_as_ulong (child);
+	}
+
+	/* A tag from the universal set */
+	switch (anode_def_type (node)) {
+	case TYPE_INTEGER:
+		return ASN1_TAG_INTEGER;
+	case TYPE_ENUMERATED:
+		return ASN1_TAG_ENUMERATED;
+	case TYPE_BOOLEAN:
+		return ASN1_TAG_BOOLEAN;
+	case TYPE_BIT_STRING:
+		return ASN1_TAG_BIT_STRING;
+	case TYPE_OCTET_STRING:
+		return ASN1_TAG_OCTET_STRING;
+	case TYPE_OBJECT_ID:
+		return ASN1_TAG_OBJECT_ID;
+	case TYPE_NULL:
+		return ASN1_TAG_NULL;
+	case TYPE_GENERALSTRING:
+		return ASN1_TAG_GENERALSTRING;
+	case TYPE_TIME:
+		if (flags & FLAG_GENERALIZED)
+			return ASN1_TAG_GENERALIZEDTime;
+		else if (flags & FLAG_UTC)
+			return ASN1_TAG_UTCTime;
+		else
+			g_return_val_if_reached (G_MAXULONG);
+	case TYPE_SEQUENCE:
+	case TYPE_SEQUENCE_OF:
+		return ASN1_TAG_SEQUENCE;
+	case TYPE_SET:
+	case TYPE_SET_OF:
+		return ASN1_TAG_SET;
+
+	/* These should be handled specially */
+	case TYPE_ANY:
+	case TYPE_CHOICE:
+		return G_MAXULONG;
+
+	/* These are not real nodes */
+	case TYPE_CONSTANT:
+	case TYPE_IDENTIFIER:
+	case TYPE_TAG:
+	case TYPE_DEFAULT:
+	case TYPE_SIZE:
+	case TYPE_DEFINITIONS:
+	case TYPE_IMPORTS:
+		g_return_val_if_reached (G_MAXULONG);
+
+	/* Unknown value */
+	default:
+		g_return_val_if_reached (G_MAXULONG);
+	}
+}
+
+static gulong
+anode_encode_tag (GNode *node)
+{
+	return anode_encode_tag_for_flags (node, anode_def_flags (node));
+}
+
+static gboolean
+anode_decode_cls_tag_len (const guchar *data, const guchar *end,
+                          guchar *cls, gulong *tag, gint *off, gint *len)
 {
 	gint cb1, cb2;
-	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);
+	gint der_len;
+	g_assert (end >= data);
+	der_len = end - data;
+	if (asn1_get_tag_der (data, der_len, cls, &cb1, tag) != ASN1_SUCCESS)
+		return FALSE;
+	*len = asn1_get_length_der (data + cb1, der_len - cb1, &cb2);
 	if (*len < -1)
-		return -1;
-	if (*len == -1)
-		*len = G_MAXINT;
-	else if (cb1 + cb2 + *len > n_data)
-		return -1;
-	return cb1 + cb2;
+		return FALSE;
+	*off = cb1 + cb2;
+	if (*len >= 0 && data + *off + *len > end)
+		return FALSE;
+	return TRUE;
+}
+
+static gboolean
+anode_check_indefinite_end (guchar cls, gulong tag, gint len)
+{
+	return (cls == ASN1_CLASS_UNIVERSAL && tag == 0 && len == 0);
 }
 
-static gssize
-anode_decode_indefinite_len (const guchar *data, gsize n_data)
+static gboolean
+anode_decode_indefinite_len (const guchar *data, const guchar *end, gint *rlen)
 {
-	gssize result = 0;
+	gint result = 0;
+	gint der_len;
 	gint len;
 	guchar cls;
 	gulong tag;
-	gssize off;
+	gint off;
 
-	while (result < n_data) {
-		off = anode_decode_cls_tag_len (data + result, n_data - result, &cls, &tag, &len);
-		if (off < 0)
-			return -1;
-		result += off;
+	g_assert (data <= end);
+	der_len = end - data;
+
+	while (result < der_len) {
+		if (!anode_decode_cls_tag_len (data + result, end, &cls, &tag, &off, &len))
+			return FALSE;
 
 		/* The indefinite end */
-		if (tag == 0x00 && cls == ASN1_CLASS_UNIVERSAL && len == 0)
+		if (anode_check_indefinite_end (cls, tag, len))
 			break;
 
+		result += off;
+
 		/* Mid way check */
-		if (result > n_data)
+		if (result > der_len)
 			break;
 
-		if (len == G_MAXINT) {
-			len = anode_decode_indefinite_len (data + result, n_data - result);
-			if (len < 0)
-				return -1;
+		if (len < 0) {
+			if (!anode_decode_indefinite_len (data + result, end, &len))
+				return FALSE;
+			g_assert (len >= 0);
 		}
 
-		if (result + len > n_data)
-			return -1;
+		if (result + len > der_len)
+			return FALSE;
 		result += len;
 	}
 
-	if (result > n_data)
-		return -1;
-	return result;
+	if (result > der_len)
+		return FALSE;
+	*rlen = result;
+	return TRUE;
 }
 
-static gssize
-anode_decode_indefinite_end (const guchar *data, gsize n_data)
+static gboolean
+anode_decode_tlv_for_data (const guchar *data, const guchar *end, Atlv *tlv)
 {
-	gint len;
-	guchar cls;
-	gulong tag;
-	gssize off;
-
-	off = anode_decode_cls_tag_len (data, n_data, &cls, &tag, &len);
-	if (off < -1)
-		return -1;
-	if (tag != 0x00 || cls != ASN1_CLASS_UNIVERSAL || len != 0)
-		return 0;
-	return off;
+	g_assert (data <= end);
+	if (!anode_decode_cls_tag_len (data, end, &tlv->cls,
+	                               &tlv->tag, &tlv->off, &tlv->len))
+		return FALSE;
+	tlv->buf = data;
+	if (tlv->len < 0)
+		tlv->end = end;
+	else
+		tlv->end = tlv->buf + tlv->len + tlv->off;
+	g_assert (tlv->end <= end);
+	return TRUE;
 }
 
-static gssize
-anode_decode_value_data (GNode *node, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_tlv_for_contents (Atlv *outer, gboolean first, Atlv *tlv)
 {
-	Anode *an = node->data;
-	Adata *ad;
+	const guchar *data;
+	const guchar *end;
+
+	if (first) {
+		data = outer->buf + outer->off;
+		end = outer->end;
+	} else {
+		data = tlv->end;
+		end = outer->end;
+	}
+
+	/* The end */
+	if (end == data) {
+		tlv->cls = ASN1_CLASS_UNIVERSAL;
+		tlv->tag = 0;
+		tlv->len = 0;
+		tlv->off = 0;
+		tlv->buf = data;
+		tlv->end = end;
+		return TRUE;
+	}
+
+	g_return_val_if_fail (end > data, FALSE);
+	if (!anode_decode_tlv_for_data (data, end, tlv))
+		return FALSE;
 
-	g_return_val_if_fail (!an->data, -1);
+	/* Caller should stop before indefinite end, and not consume */
+	if (anode_check_indefinite_end (tlv->cls, tlv->tag, tlv->len)) {
+		tlv->buf = data;
+		tlv->end = data;
+		tlv->off = 0;
+	}
+
+	return TRUE;
+}
 
-	/* All validation is done later */
-	ad = g_slice_new0 (Adata);
-	ad->buf = data;
-	ad->n_buf = n_data;
-	an->data = ad;
+static gboolean
+anode_decode_tlv_ensure_length (Atlv *tlv)
+{
+	if (tlv->len >= 0)
+		return TRUE;
 
-	return n_data;
+	if (!anode_decode_indefinite_len (tlv->buf + tlv->off, tlv->end, &tlv->len))
+		return FALSE;
+
+	g_assert (tlv->len >= 0);
+	tlv->end = tlv->buf + tlv->off + tlv->len;
+	return TRUE;
 }
 
-static gsize
-anode_decode_value_chain (GNode *node, gulong of_tag, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_choice (GNode *node, Atlv *tlv)
 {
-	Anode *an = node->data;
-	Adata *ad, **last;
-	guchar cls;
-	gulong tag;
-	gssize off, read = 0;
-	gint len;
+	GNode *child;
 
-	g_return_val_if_fail (!an->data, -1);
-
-	last = &an->data;
-	for (;;) {
-		off = anode_decode_cls_tag_len (data + read, n_data - read, &cls, &tag, &len);
-		if (off < 0)
-			return -1;
-		if (len == G_MAXINT)
-			return -1;
-		read += off;
-		if (read > n_data)
-			return -1;
-		if (len == 0)
-			break;
+	for (child = anode_child_with_real_type (node);
+	     child; child = anode_next_with_real_type (child)) {
+		if (anode_decode_anything (child, tlv))
+			return TRUE;
+	}
 
-		if (tag != of_tag)
-			return -1;
+	return FALSE;
+}
 
-		/* A new data block */
-		ad = g_slice_new0 (Adata);
-		ad->buf = data + read;
-		ad->n_buf = len;
+static gboolean
+anode_decode_struct_string (GNode *node, Atlv *outer)
+{
+	gint i = 0;
+	Atlv tlv;
 
-		/* And add it */
-		*last = ad;
-		last = &ad->next;
+	/* Recalculated below */
+	outer->len = 0;
 
-		read += len;
-		if (read > n_data)
-			return -1;
+	for (i = 0; TRUE; ++i) {
+		if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv))
+			return FALSE;
+		anode_add_tlv_data (node, &tlv);
+		outer->len = (tlv.end - outer->buf) - outer->off;
 	}
 
-	return read;
+	g_assert (outer->len >= 0);
+	return TRUE;
 }
 
-static gssize
-anode_decode_sequence (GNode *node, gboolean definite, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_sequence_or_set (GNode *node, Atlv *outer)
 {
 	GNode *child;
-	gssize read, off;
+	Atlv tlv;
+	gint i;
 
-	read = 0;
-	for (child = node->children; child; child = child->next) {
-		g_assert (read <= n_data);
-		off = anode_decode_anything (child, data + read, n_data - read);
-		if (off < 0)
-			return -1;
-		g_assert (off <= n_data - read);
-		read += off;
-	}
+	/* Recalculated below */
+	outer->len = 0;
 
-	if (!definite) {
-		off = anode_decode_indefinite_end (data + read, n_data - read);
-		if (off <= 0)
-			return -1;
-		read += off;
+	/*
+	 * The reason we can parse a set just like a sequence, is because in DER,
+	 * the order of the SET is predefined by the tags. In addition the definitions
+	 * we have are sorted.
+	 */
+
+	for (child = anode_child_with_real_type (node), i = 0;
+	     child; child = anode_next_with_real_type (child), ++i) {
+
+		if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv))
+			return FALSE;
+
+		if (!anode_decode_anything (child, &tlv))
+			return FALSE;
+
+		outer->len = (tlv.end - outer->buf) - outer->off;
 	}
 
-	return read;
+	g_assert (outer->len >= 0);
+	return TRUE;
 }
 
-static gssize
-anode_decode_sequence_or_set_of (GNode *node, gboolean definite, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_sequence_or_set_of (GNode *node, Atlv *outer)
 {
 	GNode *child, *copy;
-	gssize read, off;
+	Atlv tlv;
+	gint i;
+
+	outer->len = 0;
 
 	/* The one and only child */
-	child = anode_child_with_any_data_type (node);
+	child = anode_child_with_real_type (node);
 	g_return_val_if_fail (child, -1);
 
 	/* Try to dig out as many of them as possible */
-	read = 0;
-	for (;;) {
+	for (i = 0; TRUE; ++i) {
 
-		/* Definite length, fill up data we were passed */
-		if (definite) {
-			g_assert (read <= n_data);
-			if (read == n_data)
-				break;
+		if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv))
+			return FALSE;
 
-		/* Indefinite length, look for marker */
-		} else {
-			off = anode_decode_indefinite_end (data + read, n_data - read);
-			if (off < 0)
-				return -1;
-			read += off;
-			if (off != 0)
-				break;
-		}
+		/* The end of the road for us */
+		if (tlv.off == 0)
+			break;
 
 		copy = anode_clone (child);
-		off = anode_decode_anything (copy, data + read, n_data - read);
-		if (off < 0) {
+		if (!anode_decode_anything (copy, &tlv)) {
 			anode_destroy (copy);
-			return -1;
+			return FALSE;
 		}
-		g_return_val_if_fail (off != 0, -1);
-		g_assert (off <= n_data - read);
-		read += off;
+
 		g_node_append (node, copy);
+		outer->len = (tlv.end - outer->buf) - outer->off;
 	}
 
-	return read;
+	g_assert (outer->len >= 0);
+	return TRUE;
 }
 
-static gssize
-anode_decode_choice (GNode *node, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_primitive (GNode *node, Atlv *tlv, gint flags)
 {
-	GNode *child;
-	gssize off;
-
-	for (child = node->children; child; child = child->next) {
-		off = anode_decode_anything (child, data, n_data);
-		if (off >= 0)
-			return off;
-	}
-
-	return -1;
-}
+	gint type;
 
-static gssize
-anode_decode_value_of_len (GNode *node, guchar cls, gulong tag,
-                           const guchar *data, gsize n_data)
-{
-	gint type, flags, want;
+	/* Must have a definite length */
+	if (tlv->len < 0)
+		return FALSE;
 
 	type = anode_def_type (node);
 	switch (type) {
 
 	/* The primitive value types */
 	case TYPE_INTEGER:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_INTEGER ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_ENUMERATED:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_ENUMERATED ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_BOOLEAN:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_BOOLEAN ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_BIT_STRING:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_BIT_STRING ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_OCTET_STRING:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_OCTET_STRING ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_OBJECT_ID:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_OBJECT_ID ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_NULL:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_NULL ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
 	case TYPE_GENERALSTRING:
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_GENERALSTRING ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
 	case TYPE_TIME:
-		flags = anode_def_flags (node);
-		if (flags & FLAG_GENERALIZED)
-			want = ASN1_TAG_GENERALIZEDTime;
-		else if (flags & FLAG_UTC)
-			want = ASN1_TAG_UTCTime;
-		else
-			g_return_val_if_reached (-1);
-		if (cls != ASN1_CLASS_UNIVERSAL || tag != want ||
-		    anode_decode_value_data (node, data, n_data) < 0)
-			return -1;
-		break;
-
-	/* SEQUENCE: A sequence of child TLV's */
-	case TYPE_SEQUENCE:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_SEQUENCE ||
-		    anode_decode_sequence (node, TRUE, data, n_data) != n_data)
-			return -1;
-		break;
-
-	/* SEQUENCE OF: A sequence of one type of child TLV */
-	case TYPE_SEQUENCE_OF:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_SEQUENCE ||
-		    anode_decode_sequence_or_set_of (node, TRUE, data, n_data) != n_data)
-			return -1;
-		break;
-
-	/* SET OF: A set of one type of child TLV */
-	case TYPE_SET_OF:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_SET ||
-		    anode_decode_sequence_or_set_of (node, TRUE, data, n_data) != n_data)
-			return -1;
-		break;
+		anode_add_tlv_data (node, tlv);
+		return TRUE;
 
-	case TYPE_SET:
-		g_assert (0 && "TODO");
-		break;
+	/* Transparent types */
+	case TYPE_ANY:
+		anode_add_tlv_data (node, tlv);
+		return TRUE;
+	case TYPE_CHOICE:
+		return anode_decode_choice (node, tlv);
 
-	/* These should have been handled by caller */
 	default:
-		g_return_val_if_reached (-1);
+		return FALSE;
 	}
 
-	return n_data;
+	g_assert_not_reached ();
 }
 
-static gssize
-anode_decode_value_of_indefinite (GNode *node, guchar cls, gulong tag,
-                                  const guchar *data, gsize n_data)
+static gboolean
+anode_decode_structured (GNode *node, Atlv *tlv, gint flags)
 {
-	gint type;
+	gboolean definite;
+	const guchar *end;
+	Atlv ctlv;
 	gint len;
+	gulong tag;
+	guchar cls;
+	gint off = 0;
 
-	type = anode_def_type (node);
-	switch (type) {
+	definite = (tlv->len >= 0);
+	end = tlv->end;
 
-	/* Not supported with indefinite length */
-	case TYPE_INTEGER:
-	case TYPE_BOOLEAN:
-	case TYPE_BIT_STRING:
-	case TYPE_OBJECT_ID:
-	case TYPE_NULL:
-	case TYPE_TIME:
-	case TYPE_ENUMERATED:
-		return -1;
-
-	case TYPE_OCTET_STRING:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_OCTET_STRING)
-			return -1;
-		len = anode_decode_value_chain (node, ASN1_TAG_OCTET_STRING, data, n_data);
-		break;
-	case TYPE_GENERALSTRING:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_GENERALSTRING)
-			return -1;
-		len = anode_decode_value_chain (node, ASN1_TAG_GENERALSTRING, data, n_data);
-		break;
+	/* An explicit, wrapped tag */
+	if (flags & FLAG_TAG && !(flags & FLAG_IMPLICIT)) {
+		if (!anode_decode_tlv_for_contents (tlv, TRUE, &ctlv))
+			return FALSE;
+		flags &= ~FLAG_TAG;
+		if (!anode_decode_anything_for_flags (node, &ctlv, flags))
+			return FALSE;
+		tlv->len = ctlv.off + ctlv.len;
 
-	/* SEQUENCE: A sequence of child TLV's */
-	case TYPE_SEQUENCE:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_SEQUENCE)
-			return -1;
-		len = anode_decode_sequence (node, FALSE, data, n_data);
-		break;
+	/* Other structured types */
+	} else {
+		switch (anode_def_type (node)) {
+		case TYPE_ANY:
+			if (!anode_decode_tlv_ensure_length (tlv))
+				return FALSE;
+			anode_add_tlv_data (node, tlv);
+			break;
 
-	/* SEQUENCE OF: A sequence of one type of child TLV */
-	case TYPE_SEQUENCE_OF:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_SEQUENCE)
-			return -1;
-		len = anode_decode_sequence_or_set_of (node, FALSE, data, n_data);
-		break;
+		case TYPE_CHOICE:
+			if (!anode_decode_choice (node, tlv))
+				return FALSE;
+			break;
 
-	/* SET OF: A set of one type of child TLV */
-	case TYPE_SET_OF:
-		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
-		    tag != ASN1_TAG_SET)
-			return -1;
-		len = anode_decode_sequence_or_set_of (node, FALSE, data, n_data);
-		break;
+		case TYPE_GENERALSTRING:
+		case TYPE_OCTET_STRING:
+			if (!anode_decode_struct_string (node, tlv))
+				return FALSE;
+			break;
+		case TYPE_SEQUENCE:
+		case TYPE_SET:
+			if (!anode_decode_sequence_or_set (node, tlv))
+				return FALSE;
+			break;
+		case TYPE_SEQUENCE_OF:
+		case TYPE_SET_OF:
+			if (!anode_decode_sequence_or_set_of (node, tlv))
+				return FALSE;
+			break;
+		default:
+			return FALSE;
+		}
+	}
 
-	case TYPE_SET:
-		g_assert (0 && "TODO");
-		break;
+	g_return_val_if_fail (tlv->len >= 0, FALSE);
 
-	default:
-		g_return_val_if_reached (-1);
+	/* Indefinite, needs to be terminated with zeros */
+	if (!definite) {
+		if (!anode_decode_cls_tag_len (tlv->buf + (tlv->off + tlv->len), end,
+		                               &cls, &tag, &off, &len))
+			return FALSE;
+		if (!anode_check_indefinite_end (cls, tag, len))
+			return FALSE;
+		tlv->len += off;
+		end = tlv->buf + tlv->off + tlv->len;
 	}
 
-	return len;
+	/* A structure must be filled up, no stuff ignored */
+	if (tlv->buf + tlv->off + tlv->len != end)
+		return FALSE;
+
+	tlv->end = end;
+	return TRUE;
 }
 
-static gssize
-anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_anything_for_flags (GNode *node, Atlv *tlv, gint flags)
 {
-	guchar cls;
+	gboolean ret;
 	gulong tag;
-	gint len;
-	gssize off;
-	gint type;
 
-	type = anode_def_type (node);
+	tag = anode_encode_tag_for_flags (node, flags);
 
-	/* Certain transparent types */
-	switch (type) {
+	/* We don't know what the tag is supposed to be */
+	if (tag == G_MAXULONG)
+		tag = tlv->tag;
 
-	/* CHOICE: The entire TLV is one of children */
-	case TYPE_CHOICE:
-		return anode_decode_choice (node, data, n_data);
-
-	/* These node types should not appear here */
-	case TYPE_CONSTANT:
-	case TYPE_IDENTIFIER:
-	case TYPE_TAG:
-	case TYPE_DEFAULT:
-	case TYPE_SIZE:
-	case TYPE_DEFINITIONS:
-	case TYPE_IMPORTS:
-		g_return_val_if_reached (-1);
-	}
-
-	/* If we didn't parse the tag above */
-	off = anode_decode_cls_tag_len (data, n_data, &cls, &tag, &len);
-	if (off < 0)
-		return -1;
-
-	/* Concrete types */
-
-	/* The length is indefinite */
-	if (len == G_MAXINT) {
-
-		/* ANY: The entire TLV is the value */
-		if (type == TYPE_ANY) {
-			len = anode_decode_indefinite_len (data + off, n_data - off);
-			if (len < 0)
-				return -1;
-			return anode_decode_value_data (node, data, off + len);
-
-		/* All other concrete types */
+	/* Tag does not match, what do we do? */
+	if (tlv->off == 0 || tag != tlv->tag) {
+		if (flags & FLAG_OPTION || flags & FLAG_DEFAULT) {
+			tlv->len = 0;
+			tlv->end = tlv->buf;
+			tlv->off = 0;
+			return TRUE;
 		} else {
-			len = anode_decode_value_of_indefinite (node, cls, tag, data + off, n_data - off);
+			return FALSE;
 		}
+	}
 
-	/* The length is definite */
-	} else {
-		if (off + len > n_data)
-			return -1;
-
-		/* ANY: The entire TLV is the value */
-		if (type == TYPE_ANY) {
-			return anode_decode_value_data (node, data, off + len);
+	/* Structured value */
+	if (tlv->cls & ASN1_CLASS_STRUCTURED)
+		ret = anode_decode_structured (node, tlv, flags);
 
-		} else {
-			if (anode_decode_value_of_len (node, cls, tag, data + off, len) != len)
-				return -1;
-		}
-	}
+	/* A primitive simple value */
+	else
+		ret = anode_decode_primitive (node, tlv, flags);
 
-	return off + len;
+	return ret;
 }
 
-static gssize
-anode_decode_explicit_or_type (GNode *node, const guchar *data, gsize n_data)
+static gboolean
+anode_decode_anything (GNode *node, Atlv *tlv)
 {
-	GNode *child;
-	guchar cls;
-	gulong tag;
-	gint len;
-	gsize off;
-	gint flags;
+	return anode_decode_anything_for_flags (node, tlv, anode_def_flags (node));
+}
 
-	flags = anode_def_flags (node);
+gboolean
+egg_asn1x_decode (GNode *asn, gconstpointer data, gsize n_data)
+{
+	Atlv tlv;
 
-	/* An explicitly tagged value, is one that has a tag specially assigned. */
-	if (flags & FLAG_TAG) {
-		off = anode_decode_cls_tag_len (data, n_data, &cls, &tag, &len);
-		if (off < 0)
-			return -1;
-		if (cls != (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CLASS_STRUCTURED))
-			return -1;
-		child = anode_child_with_type (node, TYPE_TAG);
-		g_return_val_if_fail (child, -1);
-		if (tag != anode_def_value_as_ulong (child))
-			return -1;
+	g_return_val_if_fail (asn, FALSE);
+	g_return_val_if_fail (data, FALSE);
+	g_return_val_if_fail (n_data, FALSE);
 
-		/* Definite length */
-		if (len != G_MAXINT) {
-			if (anode_decode_type_and_value (node, data + off, len) != len)
-				return -1;
+	if (!anode_decode_tlv_for_data (data, (const guchar*)data + n_data, &tlv))
+		return FALSE;
 
-		/* Indefinite length */
-		} else {
-			len = anode_decode_type_and_value (node, data + off, n_data - off);
-			if (len <= 0 || off + len > n_data)
-				return -1;
-			off += len;
-			len = anode_decode_indefinite_end (data + off, n_data - off);
-			if (len <= 0 || off + len > n_data)
-				return -1;
-		}
+	if (!anode_decode_anything (asn, &tlv))
+		return FALSE;
 
-		return off + len;
-	}
+	if (tlv.buf + tlv.off + tlv.len != tlv.end)
+		return FALSE;
 
-	return anode_decode_type_and_value (node, data, n_data);
+	return TRUE;
 }
 
-static gssize
-anode_decode_anything (GNode *node, const guchar *data, gsize n_data)
+static gint
+compare_nodes_by_tag (gconstpointer a, gconstpointer b)
 {
-	gssize off;
-	int flags;
+	GNode *na = (gpointer)a;
+	GNode *nb = (gpointer)b;
+	gulong taga, tagb;
 
-	off = anode_decode_explicit_or_type (node, data, n_data);
-	if (off < 0) {
-		flags = anode_def_flags (node);
-		if (flags & FLAG_OPTION)
-			off = 0;
-		else if (flags & FLAG_DEFAULT)
-			g_assert (0 && "TODO");
-	}
-	return off;
-}
+	g_return_val_if_fail (anode_def_flags (na) & FLAG_TAG, 0);
+	g_return_val_if_fail (anode_def_flags (nb) & FLAG_TAG, 0);
 
-gboolean
-egg_asn1x_decode (GNode *asn, gconstpointer data, gsize n_data)
-{
-	gsize offset;
+	taga = anode_encode_tag (na);
+	g_return_val_if_fail (taga != G_MAXULONG, 0);
 
-	g_return_val_if_fail (asn, FALSE);
-	g_return_val_if_fail (data, FALSE);
-	g_return_val_if_fail (n_data, FALSE);
+	tagb = anode_encode_tag (nb);
+	g_return_val_if_fail (tagb != G_MAXULONG, 0);
 
-	offset = anode_decode_anything (asn, data, n_data);
-	return (offset == n_data);
+	if (taga == tagb)
+		return 0;
+	return (taga < tagb) ? -1 : 1;
 }
 
 static void
@@ -786,12 +804,14 @@ join_each_child (GNode *child, gpointer data)
 }
 
 static gboolean
-traverse_and_create_joins (GNode *node, gpointer data)
+traverse_and_prepare (GNode *node, gpointer data)
 {
 	const ASN1_ARRAY_TYPE *defs = data;
+	const gchar *identifier;
 	Anode *an, *anj;
 	GNode *join = NULL;
-	const gchar *identifier;
+	GNode *child, *next;
+	GList *list = NULL, *l;
 
 	/* A while, because the stuff we join, could also be an identifier */
 	while (anode_def_type (node) == TYPE_IDENTIFIER) {
@@ -810,6 +830,23 @@ traverse_and_create_joins (GNode *node, gpointer data)
 		egg_asn1x_destroy (join);
 	}
 
+	/* Sort the children of any sets */
+	if (anode_def_type (node) == TYPE_SET) {
+		child = node->children;
+		while (child) {
+			next = child->next;
+			if (anode_def_type_is_real (child)) {
+				g_node_unlink (child);
+				list = g_list_prepend (list, child);
+			}
+			child = next;
+		}
+		list = g_list_sort (list, compare_nodes_by_tag);
+		for (l = list; l; l = g_list_next (l))
+			g_node_append (node, l->data);
+		g_list_free (list);
+	}
+
 	/* Continue traversal */
 	return FALSE;
 }
@@ -868,8 +905,8 @@ egg_asn1x_create (const ASN1_ARRAY_TYPE *defs, const gchar *identifier)
 	}
 
 	/* Load up sub identifiers */
-	g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
-	                 traverse_and_create_joins, (gpointer)defs);
+	g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
+	                 traverse_and_prepare, (gpointer)defs);
 
 	return root;
 }
diff --git a/egg/tests/test-asn1x.c b/egg/tests/test-asn1x.c
index d8c2dd1..99f4515 100644
--- a/egg/tests/test-asn1x.c
+++ b/egg/tests/test-asn1x.c
@@ -11,6 +11,38 @@
 #include "egg/asn1-def-pk.h"
 #undef extern
 
+#if 0
+static void
+build_personal_name (void)
+{
+	ASN1_TYPE asn1_pkix = NULL, asn;
+	guchar buffer[10024];
+	int res, len;
+
+	res = asn1_array2tree (pkix_asn1_tab, &asn1_pkix, NULL);
+	g_assert (res == ASN1_SUCCESS);
+
+	res = asn1_create_element (asn1_pkix, "PKIX1.PersonalName", &asn);
+	g_assert (res == ASN1_SUCCESS);
+
+	asn1_write_value (asn, "surname", "Turanga", 7);
+	asn1_write_value (asn, "given-name", "Leela", 5);
+	asn1_write_value (asn, "initials", NULL, 0);
+	asn1_write_value (asn, "generation-qualifier", "Alien", 5);
+
+	len = sizeof (buffer);
+	res = asn1_der_coding (asn, "", buffer, &len, NULL);
+	g_assert (res == ASN1_SUCCESS);
+
+	asn1_delete_structure (&asn);
+	asn1_delete_structure (&asn1_pkix);
+
+	if (!g_file_set_contents ("/tmp/personal-name.der", (gchar*)buffer, len, NULL))
+		g_assert (FALSE);
+
+}
+#endif
+
 static int
 run (void)
 {
@@ -18,8 +50,21 @@ run (void)
 	gpointer data;
 	gsize n_data;
 
-	data = testing_data_read ("test-pkcs7-1.der", &n_data);
-	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-7-ContentInfo");
+	/* Build up a personal name, which is a set */
+#if 0
+	build_personal_name ();
+#endif
+
+	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);
+	g_free (data);
+
+	data = testing_data_read ("test-pkcs8-1.der", &n_data);
+	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-8-PrivateKeyInfo");
 	egg_asn1x_dump (asn);
 	if (!egg_asn1x_decode (asn, data, n_data))
 		g_assert_not_reached ();
@@ -34,16 +79,16 @@ run (void)
 	egg_asn1x_destroy (asn);
 	g_free (data);
 
-	data = testing_data_read ("test-pkcs8-1.der", &n_data);
-	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-8-PrivateKeyInfo");
+	data = testing_data_read ("test-personalname-1.der", &n_data);
+	asn = egg_asn1x_create (pkix_asn1_tab, "PersonalName");
 	egg_asn1x_dump (asn);
 	if (!egg_asn1x_decode (asn, data, n_data))
 		g_assert_not_reached ();
 	egg_asn1x_destroy (asn);
 	g_free (data);
 
-	data = testing_data_read ("test-certificate-1.der", &n_data);
-	asn = egg_asn1x_create (pkix_asn1_tab, "Certificate");
+	data = testing_data_read ("test-pkcs7-1.der", &n_data);
+	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-7-ContentInfo");
 	egg_asn1x_dump (asn);
 	if (!egg_asn1x_decode (asn, data, n_data))
 		g_assert_not_reached ();
diff --git a/egg/tests/test-data/test-personalname-1.der b/egg/tests/test-data/test-personalname-1.der
new file mode 100644
index 0000000..60d5d8c
--- /dev/null
+++ b/egg/tests/test-data/test-personalname-1.der
@@ -0,0 +1 @@
+1?Turanga?Leela?Alien
\ No newline at end of file



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