[gnome-keyring/asn1-work: 10/18] [egg] Rework asn1 decoding, add set support, much more robust.
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring/asn1-work: 10/18] [egg] Rework asn1 decoding, add set support, much more robust.
- Date: Thu, 24 Jun 2010 03:39:34 +0000 (UTC)
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]