summaryrefslogtreecommitdiff
path: root/egg/egg-asn1x.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2009-12-22 03:57:58 +0000
committerStef Walter <stef@memberwebs.com>2010-06-24 02:55:59 +0000
commitad956e90e0f42613f6c60729fc988ea5152fd442 (patch)
treefedb0c5bf5581182794eea8b9ef62c6667c5dd9b /egg/egg-asn1x.c
parentd175839add9021078d8bb8f8873c98e1d21a75d1 (diff)
downloadgcr-ad956e90e0f42613f6c60729fc988ea5152fd442.tar.gz
[egg] Implement DER parsing of a certificate.
Diffstat (limited to 'egg/egg-asn1x.c')
-rw-r--r--egg/egg-asn1x.c497
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);
}