[gnome-keyring/asn1-work: 5/18] [egg] Implement DER parsing of a certificate.



commit 75d59a0348d780ff45562826da820e77a3169f24
Author: Stef Walter <stef memberwebs com>
Date:   Tue Dec 22 03:57:58 2009 +0000

    [egg] Implement DER parsing of a certificate.

 egg/egg-asn1x.c |  497 ++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 305 insertions(+), 192 deletions(-)
---
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
index 0584341..0af5776 100644
--- a/egg/egg-asn1x.c
+++ b/egg/egg-asn1x.c
@@ -27,6 +27,7 @@
 
 #include <libtasn1.h>
 
+#include <stdlib.h>
 #include <string.h>
 
 enum {
@@ -95,8 +96,10 @@ typedef struct Anode {
 	gsize n_data;
 } Anode;
 
+/* TODO: Validate: LIST SIZE */
+
 /* Forward Declarations */
-static gssize anode_decode_any (GNode*, const guchar*, gsize);
+static gssize anode_decode_anything (GNode*, const guchar*, gsize);
 
 static GNode*
 anode_new (const ASN1_ARRAY_TYPE *def)
@@ -109,25 +112,53 @@ anode_new (const ASN1_ARRAY_TYPE *def)
 	return g_node_new (an);
 }
 
+static gboolean
+anode_free_func (GNode *node, gpointer unused)
+{
+	g_slice_free (Anode, node->data);
+	return FALSE;
+}
+
 static void
-anode_free (gpointer data)
+anode_destroy (GNode *node)
 {
-	if (data)
-		g_slice_free (Anode, data);
+	g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1, anode_free_func, NULL);
+	g_node_destroy (node);
+
+}
+
+static gpointer
+anode_copy_func (gconstpointer src, gpointer unused)
+{
+	const Anode *san = src;
+	Anode *an = g_slice_new0 (Anode);
+	an->def = san->def;
+	an->join = san->join;
+	return an;
+}
+
+static GNode*
+anode_clone (GNode *node)
+{
+	return g_node_copy_deep (node, anode_copy_func, NULL);
 }
 
 static int
 anode_def_type (GNode *node)
 {
 	Anode *an = node->data;
-	return an->def->type & 0xFF;
+	gint type = an->join ? an->join->type : an->def->type;
+	return type & 0xFF;
 }
 
 static int
 anode_def_flags (GNode *node)
 {
 	Anode *an = node->data;
-	return an->def->type & 0xFFFFFF00;
+	gint type = an->def->type;
+	if (an->join)
+		type |= an->join->type;
+	return type & 0xFFFFFF00;
 }
 
 static const gchar*
@@ -144,256 +175,348 @@ anode_def_value (GNode *node)
 	return an->def->value;
 }
 
-static gsize
-anode_decode_tag (int ctag, int ccls, const guchar *data, gsize n_data)
+static gulong
+anode_def_value_as_ulong (GNode *node)
 {
-	guchar cls;
-	gulong tag;
-	gint cb, len;
-	gsize offset = 0;
+	const gchar* value;
+	gchar *end = NULL;
+	gulong ulval;
+
+	value = anode_def_value (node);
+	g_return_val_if_fail (value, G_MAXULONG);
+	ulval = strtoul (value, &end, 10);
+	g_return_val_if_fail (end && !end[0], G_MAXULONG);
+	return ulval;
+}
 
-	if (asn1_get_tag_der (data, n_data, &cls, &cb, &tag) != ASN1_SUCCESS)
-		return 0;
-	if (cls != ccls || tag != ctag)
-		return 0;
+static GNode*
+anode_child_with_type (GNode *node, gint type)
+{
+	GNode *child;
 
-	offset += cb;
-	data += cb;
-	n_data -= cb;
+	for (child = node->children; child; child = child->next) {
+		if (anode_def_type (child) == type)
+			return child;
+	}
+
+	return NULL;
+}
 
-	len = asn1_get_length_der (data, n_data , &cb);
-	if (len < 0)
-		return 0;
+static GNode*
+anode_child_with_any_data_type (GNode *node)
+{
+	GNode *child;
 
-	offset += cb;
-	n_data -= cb;
-	if (len != n_data)
-		return 0;
+	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:
+			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 offset;
+	return NULL;
 }
 
-static gsize
-anode_decode_length (const guchar *data, gsize n_data)
+static gssize
+anode_decode_cls_tag_len (const guchar *data, gsize n_data,
+                          guchar *cls, gulong *tag, gsize *len)
 {
-	guchar cls;
-	gulong tag;
-	gint cb1, cb2, len;
-
-	if (asn1_get_tag_der (data, n_data, &cls, &cb1, &tag) != ASN1_SUCCESS)
+	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)
+	*len = asn1_get_length_der (data + cb1, n_data - cb1, &cb2);
+	if (*len < 0)
 		return -1;
-	return len + cb1 + cb2;
+	return cb1 + cb2;
 }
 
-static gboolean
-anode_decode_boolean (GNode *node, const guchar *data, gsize n_data)
+static gssize
+anode_decode_value_data (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;
+	/* All validation is done later */
 	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;
+	an->n_data = n_data;
 
-	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;
+	return n_data;
 }
 
-static gboolean
-anode_decode_bit_string (GNode *node, const guchar *data, gsize n_data)
+static gssize
+anode_decode_sequence (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;
-}
+	GNode *child;
+	gssize read, off;
 
-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;
-}
+	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;
+	}
 
-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;
+	return read;
 }
 
-static gboolean
-anode_decode_time (GNode *node, const guchar *data, gsize n_data)
+static gssize
+anode_decode_sequence_or_set_of (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;
-}
+	GNode *child, *copy;
+	gssize read, off;
+
+	/* The one and only child */
+	child = anode_child_with_any_data_type (node);
+	g_return_val_if_fail (child, -1);
+
+	/* Try to dig out as many of them as possible */
+	read = 0;
+	while (read < n_data) {
+		copy = anode_clone (child);
+		off = anode_decode_anything (copy, data + read, n_data - read);
+		if (off < 0) {
+			anode_destroy (copy);
+			return -1;
+		}
+		g_return_val_if_fail (off != 0, -1);
+		g_assert (off <= n_data - read);
+		read += off;
+		g_node_append (node, copy);
+	}
 
-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;
+	return read;
 }
 
-static gboolean
-anode_decode_sequence (GNode *node, const guchar *data, gsize n_data)
+static gssize
+anode_decode_choice (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;
+	gssize off;
 
 	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;
+		off = anode_decode_anything (child, data, n_data);
+		if (off >= 0) {
+			g_return_val_if_fail (off == n_data, -1);
+			return off;
+		}
 	}
 
-	return TRUE;
+	return -1;
 }
 
 static gssize
-anode_decode_any (GNode *node, const guchar *data, gsize n_data)
+anode_decode_type_and_value (GNode *node, const guchar *data, gsize n_data)
 {
-	gboolean ret;
+	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;
+
+	type = anode_def_type (node);
+	switch (type) {
 
-	switch (anode_def_type (node)) {
+	/* The primitive value types */
 	case TYPE_INTEGER:
-		ret = anode_decode_integer (node, data, n_data);
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_INTEGER ||
+		    anode_decode_value_data (node, data + off, len) < 0)
+			return -1;
 		break;
 	case TYPE_BOOLEAN:
-		ret = anode_decode_boolean (node, data, n_data);
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_BOOLEAN ||
+		    anode_decode_value_data (node, data + off, len) < 0)
+			return -1;
 		break;
 	case TYPE_BIT_STRING:
-		ret = anode_decode_bit_string (node, data, n_data);
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_BIT_STRING ||
+		    anode_decode_value_data (node, data + off, len) < 0)
+			return -1;
 		break;
 	case TYPE_OCTET_STRING:
-		ret = anode_decode_octet_string (node, data, n_data);
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_OCTET_STRING ||
+		    anode_decode_value_data (node, data + off, len) < 0)
+			return -1;
 		break;
-	case TYPE_TIME:
-		ret = anode_decode_time (node, data, n_data);
+	case TYPE_OBJECT_ID:
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_OBJECT_ID ||
+		    anode_decode_value_data (node, data + off, len) < 0)
+			return -1;
 		break;
 	case TYPE_NULL:
-		ret = anode_decode_null (node, data, n_data);
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_NULL ||
+		    anode_decode_value_data (node, data + off, len) < 0)
+			return -1;
 		break;
 	case TYPE_GENERALSTRING:
-		ret = anode_decode_generalstring (node, data, n_data);
+		if (cls != ASN1_CLASS_UNIVERSAL || tag != ASN1_TAG_GENERALSTRING ||
+		    anode_decode_value_data (node, data + off, len) < 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 + off, len) < 0)
+			return -1;
 		break;
+
+	/* SEQUENCE: A sequence of child TLV's */
 	case TYPE_SEQUENCE:
-		ret = anode_decode_sequence (node, data, n_data);
+		if (cls != (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL) ||
+		    tag != ASN1_TAG_SEQUENCE ||
+		    anode_decode_sequence (node, data + off, len) != len)
+			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, data + off, len) != len)
+			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, data + off, len) != len)
+			return -1;
+		break;
+
+	case TYPE_SET:
+		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)
+			return -1;
+		break;
+
+	/* CHOICE: The entire TLV is one of children */
+	case TYPE_CHOICE:
+		if (anode_decode_choice (node, data, off + len) != off + len)
+			return -1;
+		break;
+
+	case TYPE_ENUMERATED:
+		g_assert (0 && "TODO");
 		break;
 
+	/* These node types should not appear here */
 	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: */
-		ret = FALSE;
-		break;
+		g_return_val_if_reached (-1);
+
 	default:
-		g_assert_not_reached (); /* TODO: */
-		ret = FALSE;
-		break;
+		g_return_val_if_reached (-1);
 	}
 
-	if (!ret) {
-		/* Try to parse a context specific tag thingy */
+	return off + len;
+}
 
-		g_assert_not_reached (); /* TODO: Implement checking */
+static gssize
+anode_decode_explicit_or_type (GNode *node, const guchar *data, gsize n_data)
+{
+	GNode *child;
+	guchar cls;
+	gulong tag;
+	gsize len, offset;
+	gint flags;
+
+	flags = anode_def_flags (node);
+
+	/*
+	 * An explicitly tagged value, is one that has a tag specially assigned.
+	 * These tags cannot be parsed
+	 */
+	if (flags & FLAG_TAG) {
+		offset = anode_decode_cls_tag_len (data, n_data, &cls, &tag, &len);
+		if (offset < 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;
+		if (anode_decode_type_and_value (node, data + offset, len) != len)
+			return -1;
+		return offset + len;
 	}
 
-	return ret;
+	return anode_decode_type_and_value (node, data, n_data);
+}
+
+static gssize
+anode_decode_anything (GNode *node, const guchar *data, gsize n_data)
+{
+	gssize off;
+	int flags;
+
+	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;
 }
 
 gboolean
 egg_asn1x_decode (GNode *asn, gconstpointer data, gsize n_data)
 {
+	gsize offset;
+
 	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);
+	offset = anode_decode_anything (asn, data, n_data);
+	return (offset == n_data);
 }
 
 static void
@@ -408,13 +531,13 @@ static gboolean
 traverse_and_create_identifier (GNode *node, gpointer data)
 {
 	const ASN1_ARRAY_TYPE *defs = data;
-	Anode *an = node->data;
-	Anode *ans;
+	Anode *an, *ans;
 	GNode *seq;
 
 	if (anode_def_type (node) == TYPE_IDENTIFIER) {
 		seq = egg_asn1x_create (defs, anode_def_value (node));
 		g_return_val_if_fail (seq, TRUE);
+		an = node->data;
 		ans = seq->data;
 		an->join = ans->def;
 		g_node_children_foreach (seq, G_TRAVERSE_ALL, move_each_child, node);
@@ -534,19 +657,9 @@ egg_asn1x_dump (GNode *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);
+	if (data)
+		anode_destroy (data);
 }



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