[gnome-keyring/asn1-work: 6/18] [egg] Work on indefinite length encodings.



commit d052e00b2c79177ca88e0a3cc83d39f6a486086d
Author: Stef Walter <stef memberwebs com>
Date:   Wed Dec 23 16:00:47 2009 +0000

    [egg] Work on indefinite length encodings.

 egg/egg-asn1x.c                       |  369 ++++++++++++++++++++++++++++-----
 egg/tests/test-asn1x.c                |   11 +-
 egg/tests/test-data/test-pkcs12-1.der |  Bin 0 -> 2824 bytes
 3 files changed, 324 insertions(+), 56 deletions(-)
---
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
index 0af5776..407a142 100644
--- a/egg/egg-asn1x.c
+++ b/egg/egg-asn1x.c
@@ -88,12 +88,16 @@ enum {
 	FLAG_RIGHT = (1<<30),
 };
 
-typedef struct Anode {
+typedef struct _Adata {
+	gconstpointer buf;
+	gsize n_buf;
+	struct _Adata *next;
+} Adata;
+
+typedef struct _Anode {
 	const ASN1_ARRAY_TYPE *def;
 	const ASN1_ARRAY_TYPE *join;
-	gint state;
-	gconstpointer data;
-	gsize n_data;
+	Adata *data;
 } Anode;
 
 /* TODO: Validate: LIST SIZE */
@@ -106,16 +110,17 @@ anode_new (const ASN1_ARRAY_TYPE *def)
 {
 	Anode *an = g_slice_new0 (Anode);
 	an->def = def;
-	an->state = NO_VALUE;
 	an->data = NULL;
-	an->n_data = 0;
 	return g_node_new (an);
 }
 
 static gboolean
 anode_free_func (GNode *node, gpointer unused)
 {
-	g_slice_free (Anode, node->data);
+	Anode *an = node->data;
+	if (an->data);
+		g_slice_free_chain (Adata, an->data, next);
+	g_slice_free (Anode, an);
 	return FALSE;
 }
 
@@ -243,31 +248,140 @@ anode_child_with_any_data_type (GNode *node)
 
 static gssize
 anode_decode_cls_tag_len (const guchar *data, gsize n_data,
-                          guchar *cls, gulong *tag, gsize *len)
+                          guchar *cls, gulong *tag, 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);
-	if (*len < 0)
+	if (*len < -1)
+		return -1;
+	if (*len == -1)
+		*len = G_MAXINT;
+	else if (cb1 + cb2 + *len > n_data)
 		return -1;
 	return cb1 + cb2;
 }
 
 static gssize
+anode_decode_indefinite_len (const guchar *data, gsize n_data)
+{
+	gssize result = 0;
+	gint len;
+	guchar cls;
+	gulong tag;
+	gssize 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;
+
+		/* The indefinite end */
+		if (tag == 0x00 && cls == ASN1_CLASS_UNIVERSAL && len == 0)
+			break;
+
+		/* Mid way check */
+		if (result > n_data)
+			break;
+
+		if (len == G_MAXINT) {
+			len = anode_decode_indefinite_len (data + result, n_data - result);
+			if (len < 0)
+				return -1;
+		}
+
+		if (result + len > n_data)
+			return -1;
+		result += len;
+	}
+
+	if (result > n_data)
+		return -1;
+	return result;
+}
+
+static gssize
+anode_decode_indefinite_end (const guchar *data, gsize n_data)
+{
+	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;
+}
+
+static gssize
 anode_decode_value_data (GNode *node, const guchar *data, gsize n_data)
 {
 	Anode *an = node->data;
+	Adata *ad;
+
+	g_return_val_if_fail (!an->data, -1);
 
 	/* All validation is done later */
-	an->data = data;
-	an->n_data = n_data;
+	ad = g_slice_new0 (Adata);
+	ad->buf = data;
+	ad->n_buf = n_data;
+	an->data = ad;
 
 	return n_data;
 }
 
+static gsize
+anode_decode_value_chain (GNode *node, gulong of_tag, const guchar *data, gsize n_data)
+{
+	Anode *an = node->data;
+	Adata *ad, **last;
+	guchar cls;
+	gulong tag;
+	gssize off, read = 0;
+	gint len;
+
+	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;
+
+		if (tag != of_tag)
+			return -1;
+
+		/* A new data block */
+		ad = g_slice_new0 (Adata);
+		ad->buf = data + read;
+		ad->n_buf = len;
+
+		/* And add it */
+		*last = ad;
+		last = &ad->next;
+
+		read += len;
+		if (read > n_data)
+			return -1;
+	}
+
+	return read;
+}
+
 static gssize
-anode_decode_sequence (GNode *node, const guchar *data, gsize n_data)
+anode_decode_sequence (GNode *node, gboolean definite, const guchar *data, gsize n_data)
 {
 	GNode *child;
 	gssize read, off;
@@ -282,11 +396,18 @@ anode_decode_sequence (GNode *node, const guchar *data, gsize n_data)
 		read += off;
 	}
 
+	if (!definite) {
+		off = anode_decode_indefinite_end (data + read, n_data - read);
+		if (off <= 0)
+			return -1;
+		read += off;
+	}
+
 	return read;
 }
 
 static gssize
-anode_decode_sequence_or_set_of (GNode *node, const guchar *data, gsize n_data)
+anode_decode_sequence_or_set_of (GNode *node, gboolean definite, const guchar *data, gsize n_data)
 {
 	GNode *child, *copy;
 	gssize read, off;
@@ -297,7 +418,24 @@ anode_decode_sequence_or_set_of (GNode *node, const guchar *data, gsize n_data)
 
 	/* Try to dig out as many of them as possible */
 	read = 0;
-	while (read < n_data) {
+	for (;;) {
+
+		/* Definite length, fill up data we were passed */
+		if (definite) {
+			g_assert (read <= n_data);
+			if (read == n_data)
+				break;
+
+		/* 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;
+		}
+
 		copy = anode_clone (child);
 		off = anode_decode_anything (copy, data + read, n_data - read);
 		if (off < 0) {
@@ -331,18 +469,10 @@ anode_decode_choice (GNode *node, const guchar *data, gsize n_data)
 }
 
 static gssize
-anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
+anode_decode_value_of_len (GNode *node, guchar cls, gulong tag,
+                           const guchar *data, gsize n_data)
 {
-	gint type;
-	guchar cls;
-	gulong tag;
-	gsize len;
-	gssize off;
-	gint want, flags;
-
-	off = anode_decode_cls_tag_len (data, n_data, &cls, &tag, &len);
-	if (off < 0)
-		return -1;
+	gint type, flags, want;
 
 	type = anode_def_type (node);
 	switch (type) {
@@ -350,37 +480,42 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 	/* The primitive value types */
 	case TYPE_INTEGER:
 		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_INTEGER ||
-		    anode_decode_value_data (node, data + off, len) < 0)
+		    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 + off, len) < 0)
+		    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 + off, len) < 0)
+		    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 + off, len) < 0)
+		    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 + off, len) < 0)
+		    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 + off, len) < 0)
+		    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 + off, len) < 0)
+		    anode_decode_value_data (node, data, n_data) < 0)
 			return -1;
 	case TYPE_TIME:
 		flags = anode_def_flags (node);
@@ -391,7 +526,7 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 		else
 			g_return_val_if_reached (-1);
 		if (cls != ASN1_CLASS_UNIVERSAL || tag != want ||
-		    anode_decode_value_data (node, data + off, len) < 0)
+		    anode_decode_value_data (node, data, n_data) < 0)
 			return -1;
 		break;
 
@@ -399,7 +534,7 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 	case TYPE_SEQUENCE:
 		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
 		    tag != ASN1_TAG_SEQUENCE ||
-		    anode_decode_sequence (node, data + off, len) != len)
+		    anode_decode_sequence (node, TRUE, data, n_data) != n_data)
 			return -1;
 		break;
 
@@ -407,7 +542,7 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 	case TYPE_SEQUENCE_OF:
 		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
 		    tag != ASN1_TAG_SEQUENCE ||
-		    anode_decode_sequence_or_set_of (node, data + off, len) != len)
+		    anode_decode_sequence_or_set_of (node, TRUE, data, n_data) != n_data)
 			return -1;
 		break;
 
@@ -415,7 +550,7 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 	case TYPE_SET_OF:
 		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
 		    tag != ASN1_TAG_SET ||
-		    anode_decode_sequence_or_set_of (node, data + off, len) != len)
+		    anode_decode_sequence_or_set_of (node, TRUE, data, n_data) != n_data)
 			return -1;
 		break;
 
@@ -423,22 +558,100 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 		g_assert (0 && "TODO");
 		break;
 
-	/* ANY: The entire TLV is the value */
-	case TYPE_ANY:
-		if (anode_decode_value_data (node, data, off + len) < 0)
+	/* These should have been handled by caller */
+	default:
+		g_return_val_if_reached (-1);
+	}
+
+	return n_data;
+}
+
+static gssize
+anode_decode_value_of_indefinite (GNode *node, guchar cls, gulong tag,
+                                  const guchar *data, gsize n_data)
+{
+	gint type;
+	gint len;
+
+	type = anode_def_type (node);
+	switch (type) {
+
+	/* 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;
+
+	/* 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;
 
-	/* CHOICE: The entire TLV is one of children */
-	case TYPE_CHOICE:
-		if (anode_decode_choice (node, data, off + len) != off + len)
+	/* 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_ENUMERATED:
+	/* 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_SET:
 		g_assert (0 && "TODO");
 		break;
 
+	default:
+		g_return_val_if_reached (-1);
+	}
+
+	return len;
+}
+
+static gssize
+anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
+{
+	guchar cls;
+	gulong tag;
+	gint len;
+	gssize off;
+	gint type;
+
+	type = anode_def_type (node);
+
+	/* Certain transparent types */
+	switch (type) {
+
+	/* 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:
@@ -448,9 +661,43 @@ anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 	case TYPE_DEFINITIONS:
 	case TYPE_IMPORTS:
 		g_return_val_if_reached (-1);
+	}
 
-	default:
-		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 */
+		} else {
+			len = anode_decode_value_of_indefinite (node, cls, tag, data + off, n_data - off);
+		}
+
+	/* 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);
+
+		} else {
+			if (anode_decode_value_of_len (node, cls, tag, data + off, len) != len)
+				return -1;
+		}
 	}
 
 	return off + len;
@@ -462,18 +709,16 @@ anode_decode_explicit_or_type (GNode *node, const guchar *data, gsize n_data)
 	GNode *child;
 	guchar cls;
 	gulong tag;
-	gsize len, offset;
+	gint len;
+	gsize off;
 	gint flags;
 
 	flags = anode_def_flags (node);
 
-	/*
-	 * An explicitly tagged value, is one that has a tag specially assigned.
-	 * These tags cannot be parsed
-	 */
+	/* An explicitly tagged value, is one that has a tag specially assigned. */
 	if (flags & FLAG_TAG) {
-		offset = anode_decode_cls_tag_len (data, n_data, &cls, &tag, &len);
-		if (offset < 0)
+		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;
@@ -481,9 +726,23 @@ anode_decode_explicit_or_type (GNode *node, const guchar *data, gsize n_data)
 		g_return_val_if_fail (child, -1);
 		if (tag != anode_def_value_as_ulong (child))
 			return -1;
-		if (anode_decode_type_and_value (node, data + offset, len) != len)
-			return -1;
-		return offset + len;
+
+		/* Definite length */
+		if (len != G_MAXINT) {
+			if (anode_decode_type_and_value (node, data + off, len) != len)
+				return -1;
+
+		/* 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;
+		}
+		return off + len;
 	}
 
 	return anode_decode_type_and_value (node, data, n_data);
diff --git a/egg/tests/test-asn1x.c b/egg/tests/test-asn1x.c
index 906c065..330a18e 100644
--- a/egg/tests/test-asn1x.c
+++ b/egg/tests/test-asn1x.c
@@ -17,13 +17,22 @@ run (void)
 	gpointer data;
 	gsize n_data;
 
-	data = testing_data_read ("test-certificate-1.der", &n_data);
+	data = testing_data_read ("test-pkcs12-1.der", &n_data);
+	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-12-PFX");
+	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");
 	egg_asn1x_dump (asn);
 	if (!egg_asn1x_decode (asn, data, n_data))
 		g_assert_not_reached ();
 	egg_asn1x_destroy (asn);
+	g_free (data);
+
 	return 0;
 }
 
diff --git a/egg/tests/test-data/test-pkcs12-1.der b/egg/tests/test-data/test-pkcs12-1.der
new file mode 100644
index 0000000..285d8dd
Binary files /dev/null and b/egg/tests/test-data/test-pkcs12-1.der differ



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