[gcr] egg: Rework how DER parsing works



commit ae6f001f386df172f966e055516c0b968379804a
Author: Stef Walter <stefw gnome org>
Date:   Wed Jun 20 08:15:20 2012 +0200

    egg: Rework how DER parsing works
    
     In particular fix things like
     * Indefinite parsing
     * Encoding of defaults
     * Ability to read values that haven't yet been encoded
     * Proper handling of ANY tags

 egg/egg-asn1x.c                               | 3170 +++++++++++++------------
 egg/egg-asn1x.h                               |   68 +-
 egg/egg-dn.c                                  |   50 +-
 egg/egg-dn.h                                  |    4 +-
 egg/egg-symkey.c                              |   74 +-
 egg/egg-symkey.h                              |    4 +-
 egg/tests/Makefile.am                         |   13 +-
 egg/tests/files/test-personalname-1.der       |    2 +-
 egg/tests/files/test-personalname-invalid.der |    1 +
 egg/tests/files/test-pkcs12-2.der             |  Bin 0 -> 2142 bytes
 egg/tests/test-asn1.c                         |   91 +-
 egg/tests/test-asn1x.c                        |  140 +-
 egg/tests/test-dn.c                           |   19 +-
 egg/tests/test.asn                            |    3 +-
 gcr/gcr-certificate-extensions.c              |   14 +-
 gcr/gcr-certificate-renderer.c                |    4 +-
 gcr/gcr-certificate-request.c                 |    7 +-
 gcr/gcr-parser.c                              |  190 +-
 gcr/gcr-subject-public-key.c                  |   30 +-
 gcr/tests/files/usr0052-firefox.p12           |  Bin 0 -> 2142 bytes
 gcr/tests/test-parser.c                       |    3 +
 21 files changed, 2001 insertions(+), 1886 deletions(-)
---
diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c
index 0fabb74..6fcd48e 100644
--- a/egg/egg-asn1x.c
+++ b/egg/egg-asn1x.c
@@ -56,25 +56,28 @@
 
 /* From libtasn1's libtasn.h */
 
-#define ASN1_CLASS_UNIVERSAL		0x00
-#define ASN1_CLASS_APPLICATION		0x40
-#define ASN1_CLASS_CONTEXT_SPECIFIC	0x80
-#define ASN1_CLASS_PRIVATE		0xC0
-#define ASN1_CLASS_STRUCTURED		0x20
-
-#define ASN1_TAG_BOOLEAN		0x01
-#define ASN1_TAG_INTEGER		0x02
-#define ASN1_TAG_SEQUENCE		0x10
-#define ASN1_TAG_SET			0x11
-#define ASN1_TAG_OCTET_STRING		0x04
-#define ASN1_TAG_BIT_STRING		0x03
-#define ASN1_TAG_UTCTime		0x17
-#define ASN1_TAG_GENERALIZEDTime	0x18
-#define ASN1_TAG_OBJECT_ID		0x06
-#define ASN1_TAG_ENUMERATED		0x0A
-#define ASN1_TAG_NULL			0x05
-#define ASN1_TAG_GENERALSTRING		0x1B
+enum {
+	ASN1_CLASS_UNIVERSAL = 0x00,
+	ASN1_CLASS_APPLICATION = 0x40,
+	ASN1_CLASS_CONTEXT_SPECIFIC = 0x80,
+	ASN1_CLASS_PRIVATE = 0xC0,
+	ASN1_CLASS_STRUCTURED = 0x20,
+};
 
+enum {
+	ASN1_TAG_BOOLEAN = 0x01,
+	ASN1_TAG_INTEGER = 0x02,
+	ASN1_TAG_SEQUENCE = 0x10,
+	ASN1_TAG_SET = 0x11,
+	ASN1_TAG_OCTET_STRING = 0x04,
+	ASN1_TAG_BIT_STRING = 0x03,
+	ASN1_TAG_UTC_TIME = 0x17,
+	ASN1_TAG_GENERALIZED_TIME = 0x18,
+	ASN1_TAG_OBJECT_ID = 0x06,
+	ASN1_TAG_ENUMERATED = 0x0A,
+	ASN1_TAG_NULL = 0x05,
+	ASN1_TAG_GENERALSTRING = 0x1B,
+};
 
 /* From libtasn1's int.h */
 
@@ -104,31 +107,30 @@ enum {
 	FLAG_RIGHT = (1<<30),
 };
 
-typedef gboolean (*Aencoder) (gpointer data,
-                              GNode *node,
-                              guchar *buf,
-                              gsize n_buf);
-
-typedef struct _Aenc Aenc;
 typedef struct _Atlv Atlv;
 typedef struct _Anode Anode;
-typedef struct _Abuf Abuf;
-typedef struct _Abits Abits;
-
-struct _Aenc {
-	Aencoder encoder;
-	gpointer data;
-	GDestroyNotify destroy;
-};
 
 struct _Atlv {
 	guchar cls;
 	gulong tag;
 	gint off;
-	gint oft;
 	gint len;
-	const guchar *buf;
-	const guchar *end;
+
+	/* An actual value here */
+	GBytes *value;
+
+	/* Reference to what was decoded */
+	GBytes *decoded;
+
+	/* Chain this into a tree */
+	struct _Atlv *child;
+	struct _Atlv *next;
+
+	/* Used during encoding */
+	guint bits_empty : 3;
+	guint prefix_for_bit_string : 1;
+	guint prefix_with_zero_byte : 1;
+	guint sorted : 1;
 };
 
 struct _Anode {
@@ -136,31 +138,23 @@ struct _Anode {
 	const EggAsn1xDef *join;
 	GList *opts;
 
-	Atlv *tlv;
-	Aenc *enc;
+	GBytes *value;
+	Atlv *parsed;
 
-	GBytes *backing;
 	gchar* failure;
 
-	gint chosen : 1;
-};
-
-struct _Abuf {
-	guchar* data;
-	gsize n_data;
-	gpointer user_data;
-};
-
-struct _Abits {
-	guint n_bits;
-	GBytes *bits;
+	guint chosen : 1;
+	guint bits_empty : 3;
+	guint guarantee_unsigned : 1;
 };
 
 /* Forward Declarations */
-static gboolean anode_decode_anything (GNode*, GBytes*, Atlv*);
-static gboolean anode_decode_anything_for_flags (GNode *, GBytes*, Atlv*, gint);
-static gboolean anode_validate_anything (GNode*, gboolean);
-static gboolean anode_encode_prepare (GNode*, gboolean want);
+static gboolean anode_decode_anything (GNode *, Atlv *);
+static gboolean anode_decode_one (GNode *, Atlv *);
+static GBytes * anode_default_boolean (GNode *node);
+static GBytes * anode_default_integer (GNode *node);
+static gboolean anode_validate_anything (GNode *, gboolean);
+static Atlv * anode_build_anything (GNode*, gboolean want);
 
 static gint
 atoin (const char *p, gint digits)
@@ -175,6 +169,55 @@ atoin (const char *p, gint digits)
 	return ret;
 }
 
+static const guchar *
+bytes_get_end (GBytes *data)
+{
+	const guchar *beg;
+	gsize size;
+	beg = g_bytes_get_data (data, &size);
+	return beg + size;
+}
+
+typedef struct {
+	EggAllocator allocator;
+	gpointer allocated;
+} AllocatorClosure;
+
+static void
+allocator_closure_free (gpointer data)
+{
+	AllocatorClosure *closure = data;
+	g_assert (closure->allocator);
+	(closure->allocator) (closure->allocated, 0);
+	g_slice_free (AllocatorClosure, closure);
+}
+
+static GBytes *
+bytes_new_with_allocator (EggAllocator allocator,
+                          guchar **data,
+                          gsize length)
+{
+	AllocatorClosure *closure;
+
+	if (allocator == g_realloc)
+		allocator = NULL;
+
+	if (allocator) {
+		*data = (allocator) (NULL, length + 1);
+		if (allocator == NULL)
+			return NULL;
+		closure = g_slice_new (AllocatorClosure);
+		closure->allocated = *data;
+		closure->allocator = allocator;
+		return g_bytes_new_with_free_func (*data, length,
+		                                   allocator_closure_free,
+		                                   closure);
+	} else {
+		*data = g_malloc (length);
+		return g_bytes_new_take (*data, length);
+	}
+}
+
 static GNode*
 anode_new (const EggAsn1xDef *def)
 {
@@ -357,110 +400,97 @@ anode_opts_lookup (GNode *node, gint type, const gchar *name)
 	return g_list_reverse (res);
 }
 
-static gint
-compare_tlvs (Atlv *tlva, Atlv *tlvb)
+static Atlv *
+atlv_new (void)
 {
-	gint la = tlva->off + tlva->len;
-	gint lb = tlvb->off + tlvb->len;
-	gint res;
-
-	g_assert (tlva->buf);
-	g_assert (tlvb->buf);
-	res = memcmp (tlva->buf, tlvb->buf, MIN (la, lb));
-	if (la == lb || res != 0)
-		return res;
-	return la < lb ? -1 : 1;
+	return g_slice_new0 (Atlv);
 }
 
-static inline GBytes *
-anode_get_backing (GNode *node)
+static void
+atlv_free (Atlv *tlv)
 {
-	Anode *an = node->data;
-	return an->backing;
-}
+	if (!tlv)
+		return;
 
-static inline void
-anode_clr_backing (GNode *node)
-{
-	Anode *an = node->data;
-	if (an->backing)
-		g_bytes_unref (an->backing);
-	an->backing = NULL;
-}
+	/* Free attached TLVs */
+	atlv_free (tlv->child);
+	atlv_free (tlv->next);
 
-static inline void
-anode_set_backing (GNode *node,
-                   GBytes *backing)
-{
-	Anode *an = node->data;
-	if (backing)
-		g_bytes_ref (backing);
-	if (an->backing)
-		g_bytes_unref (an->backing);
-	an->backing = backing;
+	/* Free the TLV */
+	if (tlv->decoded)
+		g_bytes_unref (tlv->decoded);
+	if (tlv->value)
+		g_bytes_unref (tlv->value);
+
+	g_slice_free (Atlv, tlv);
 }
 
-static void
-anode_set_tlv_data (GNode *node,
-                    GBytes *backing,
-                    Atlv *tlv)
+static Atlv *
+atlv_dup (Atlv *tlv,
+          gboolean siblings)
 {
-	Anode *an = node->data;
-	g_assert (an->tlv == NULL);
-	g_assert (tlv->len >= 0);
-	anode_set_backing (node, backing);
-	an->tlv = g_slice_new0 (Atlv);
-	memcpy (an->tlv, tlv, sizeof (Atlv));
+	Atlv *copy;
+
+	if (!tlv)
+		return NULL;
+
+	copy = g_slice_new0 (Atlv);
+	memcpy (copy, tlv, sizeof (Atlv));
+
+	if (tlv->value != NULL)
+		copy->value = g_bytes_ref (tlv->value);
+	if (tlv->decoded != NULL)
+		copy->decoded = g_bytes_ref (tlv->decoded);
+
+	copy->child = atlv_dup (tlv->child, TRUE);
+	if (siblings)
+		copy->next = atlv_dup (tlv->next, TRUE);
+	else
+		copy->next = NULL;
+
+	return copy;
 }
 
-static inline Atlv *
-anode_get_tlv_data (GNode *node)
+static inline GBytes *
+anode_get_value (GNode *node)
 {
 	Anode *an = node->data;
-	return an->tlv;
+	return an->value;
 }
 
-static void
-anode_clr_tlv_data (GNode *node)
+static inline void
+anode_clr_value (GNode *node)
 {
 	Anode *an = node->data;
-	if (an->tlv);
-		g_slice_free (Atlv, an->tlv);
-	an->tlv = NULL;
+	if (an->value)
+		g_bytes_unref (an->value);
+	an->value = NULL;
+
+	atlv_free (an->parsed);
+	an->parsed = NULL;
 }
 
-static void
-anode_clr_enc_data (GNode *node)
+static inline void
+anode_take_value (GNode *node,
+                  GBytes *value)
 {
 	Anode *an = node->data;
-	if (an->enc) {
-		if (an->enc->destroy)
-			(an->enc->destroy) (an->enc->data);
-		g_slice_free (Aenc, an->enc);
-		an->enc = NULL;
-	}
+	anode_clr_value (node);
+	an->value = value;
 }
 
-static void
-anode_set_enc_data (GNode *node,
-                    Aencoder encoder,
-                    gpointer data,
-                    GDestroyNotify destroy)
+static inline void
+anode_set_value (GNode *node,
+                 GBytes *value)
 {
-	Anode *an = node->data;
-	g_assert (!an->enc);
-	an->enc = g_slice_new0 (Aenc);
-	an->enc->encoder = encoder;
-	an->enc->data = data;
-	an->enc->destroy = destroy;
-	anode_clr_backing (node);
+	anode_take_value (node, g_bytes_ref (value));
 }
 
-static Aenc*
-anode_get_enc_data (GNode *node)
+static inline Atlv *
+anode_get_parsed (GNode *node)
 {
 	Anode *an = node->data;
-	return an->enc;
+	return an->parsed;
 }
 
 static gboolean
@@ -493,9 +523,7 @@ static void
 anode_clear (GNode *node)
 {
 	Anode *an = node->data;
-	anode_clr_backing (node);
-	anode_clr_tlv_data (node);
-	anode_clr_enc_data (node);
+	anode_clr_value (node);
 	g_free (an->failure);
 	an->failure = NULL;
 }
@@ -511,16 +539,6 @@ anode_free_func (GNode *node, gpointer unused)
 }
 
 static void
-abits_destroy (gpointer data)
-{
-	Abits *ab = data;
-	g_assert (ab != NULL);
-	if (ab->bits)
-		g_bytes_unref (ab->bits);
-	g_slice_free (Abits, ab);
-}
-
-static void
 anode_destroy (GNode *node)
 {
 	if (!G_NODE_IS_ROOT (node))
@@ -561,9 +579,9 @@ anode_calc_tag_for_flags (GNode *node, gint flags)
 		return ASN1_TAG_GENERALSTRING;
 	case EGG_ASN1X_TIME:
 		if (flags & FLAG_GENERALIZED)
-			return ASN1_TAG_GENERALIZEDTime;
+			return ASN1_TAG_GENERALIZED_TIME;
 		else if (flags & FLAG_UTC)
-			return ASN1_TAG_UTCTime;
+			return ASN1_TAG_UTC_TIME;
 		else
 			g_return_val_if_reached (G_MAXULONG);
 	case EGG_ASN1X_SEQUENCE:
@@ -625,47 +643,43 @@ anode_calc_explicit_for_flags (GNode *node,
 	return TRUE;
 }
 
-static gboolean
-anode_calc_explicit (GNode *node,
-                     guchar *cls_type)
-{
-	return anode_calc_explicit_for_flags (node, anode_def_flags (node), cls_type);
-}
-
 /* -------------------------------------------------------------------------
- * DECODE
+ * PARSING
  */
 
 static gboolean
-anode_decode_cls_tag (const guchar *data, const guchar *end,
-                      guchar *cls, gulong *tag, gint *cb)
+atlv_parse_cls_tag (const guchar *at,
+                    const guchar *end,
+                    guchar *cls,
+                    gulong *tag,
+                    gint *off)
 {
 	gint punt, ris, last;
 	gint n_data;
 
-	g_assert (end >= data);
-	g_assert (cls);
-	g_assert (cb);
+	g_assert (end >= at);
+	g_assert (cls != NULL);
+	g_assert (off != NULL);
 
-	n_data = end - data;
+	n_data = end - at;
 
 	if (n_data < 2)
 		return FALSE;
 
-	*cls = data[0] & 0xE0;
+	*cls = at[0] & 0xE0;
 
 	/* short form */
-	if ((data[0] & 0x1F) != 0x1F) {
-		*cb = 1;
-		ris = data[0] & 0x1F;
+	if ((at[0] & 0x1F) != 0x1F) {
+		*off = 1;
+		ris = at[0] & 0x1F;
 
 	/* Long form */
 	} else {
 		punt = 1;
 		ris = 0;
-		while (punt <= n_data && data[punt] & 128) {
+		while (punt <= n_data && at[punt] & 128) {
 			int last = ris;
-			ris = ris * 128 + (data[punt++] & 0x7F);
+			ris = ris * 128 + (at[punt++] & 0x7F);
 
 			/* wrapper around, and no bignums... */
 			if (ris < last)
@@ -676,13 +690,13 @@ anode_decode_cls_tag (const guchar *data, const guchar *end,
 			return FALSE;
 
 		last = ris;
-		ris = ris * 128 + (data[punt++] & 0x7F);
+		ris = ris * 128 + (at[punt++] & 0x7F);
 
 		/* wrapper around, and no bignums... */
 		if (ris < last)
 			return FALSE;
 
-		*cb = punt;
+		*off = punt;
 	}
 
 	if (tag)
@@ -692,31 +706,33 @@ anode_decode_cls_tag (const guchar *data, const guchar *end,
 }
 
 static gint
-anode_decode_length (const guchar *data, const guchar *end, gint *cb)
+atlv_parse_length (const guchar *at,
+                   const guchar *end,
+                   gint *off)
 {
 	gint ans, last;
 	gint k, punt;
 	gint n_data;
 
-	g_assert (data);
-	g_assert (end);
-	g_assert (end >= data);
-	g_assert (cb);
+	g_assert (at != NULL);
+	g_assert (end != NULL);
+	g_assert (end >= at);
+	g_assert (off != NULL);
 
-	*cb = 0;
-	n_data = end - data;
+	*off = 0;
+	n_data = end - at;
 
 	if (n_data == 0)
 		return 0;
 
 	/* short form */
-	if (!(data[0] & 128)) {
-		*cb = 1;
-		return data[0];
+	if (!(at[0] & 128)) {
+		*off = 1;
+		return at[0];
 
 	/* Long form */
 	} else {
-		k = data[0] & 0x7F;
+		k = at[0] & 0x7F;
 		punt = 1;
 
 		/* definite length method */
@@ -724,7 +740,7 @@ anode_decode_length (const guchar *data, const guchar *end, gint *cb)
 			ans = 0;
 			while (punt <= k && punt < n_data) {
 				last = ans;
-				ans = ans * 256 + data[punt++];
+				ans = ans * 256 + at[punt++];
 
 				/* we wrapped around, no bignum support... */
 				if (ans < last)
@@ -736,142 +752,160 @@ anode_decode_length (const guchar *data, const guchar *end, gint *cb)
 			ans = -1;
 		}
 
-		*cb = punt;
+		*off = punt;
 		return ans;
 	}
 }
 
 static gboolean
-anode_decode_cls_tag_len (const guchar *data, const guchar *end,
-                          guchar *cls, gulong *tag, gint *off, gint *len)
+atlv_parse_cls_tag_len (const guchar *at,
+                        const guchar *end,
+                        guchar *cls,
+                        gulong *tag,
+                        gint *off,
+                        gint *len)
 {
 	gint cb1, cb2;
 
-	g_assert (data);
-	g_assert (end);
-	g_assert (end >= data);
-	g_assert (off);
-	g_assert (len);
+	g_assert (at != NULL);
+	g_assert (end != NULL);
+	g_assert (end >= at);
+	g_assert (off != NULL);
+	g_assert (len != NULL);
 
-	if (!anode_decode_cls_tag (data, end, cls, tag, &cb1))
+	if (!atlv_parse_cls_tag (at, end, cls, tag, &cb1))
 		return FALSE;
-	*len = anode_decode_length (data + cb1, end, &cb2);
+	*len = atlv_parse_length (at + cb1, end, &cb2);
 	if (*len < -1)
 		return FALSE;
 	*off = cb1 + cb2;
-	if (*len >= 0 && data + *off + *len > end)
+	if (*len >= 0 && at + *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 gboolean
-anode_decode_indefinite_len (const guchar *data, const guchar *end, gint *rlen)
+static const gchar *
+atlv_parse_der_tag (guchar cls,
+                    gulong tag,
+                    gint off,
+                    gint len,
+                    GBytes *data,
+                    const guchar **at,
+                    Atlv *tlv)
 {
-	gint result = 0;
-	gint der_len;
-	gint len;
-	guchar cls;
-	gulong tag;
-	gint 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 (anode_check_indefinite_end (cls, tag, len))
-			break;
-
-		result += off;
+	const guchar *end;
+	const gchar *ret;
+	const guchar *beg;
+	guchar ccls;
+	gulong ctag;
+	gint clen;
+	gint coff;
+	Atlv *child;
+	Atlv *last;
+
+	g_assert (at != NULL);
+	g_assert (tlv != NULL);
+
+	end = bytes_get_end (data);
+	g_assert (*at <= end);
+
+	if (*at + off + len > end)
+		return "invalid length of tlv";
+	if (len < 0 && !(cls & ASN1_CLASS_STRUCTURED))
+		return "indefinite length on non-structured type";
+
+	beg = *at;
+
+	tlv->cls = cls;
+	tlv->tag = tag;
+	tlv->off = off;
+	tlv->len = len;
+	(*at) += off;
+
+	/* Structured TLV, with further TLVs inside */
+	if (cls & ASN1_CLASS_STRUCTURED) {
+		/* If not indefinite length, then calculate end up front */
+		if (len >= 0)
+			end = (*at) + len;
+		last = NULL;
+		while (*at < end) {
+			if (!atlv_parse_cls_tag_len (*at, end, &ccls, &ctag, &coff, &clen))
+				return "content is not encoded properly";
+
+			/* End if indefinite length? */
+			if (len < 0 && ccls == ASN1_CLASS_UNIVERSAL && ctag == 0 && clen == 0) {
+				(*at) += coff;
+				break;
+			}
 
-		/* Mid way check */
-		if (result > der_len)
-			break;
+			/* Parse the child */
+			child = atlv_new ();
+			ret = atlv_parse_der_tag (ccls, ctag, coff, clen, data, at, child);
+			if (ret != NULL) {
+				atlv_free (child);
+				return ret;
+			}
 
-		if (len < 0) {
-			if (!anode_decode_indefinite_len (data + result, end, &len))
-				return FALSE;
-			g_assert (len >= 0);
+			/* Add the child to the right place */
+			if (last == NULL)
+				tlv->child = child;
+			else
+				last->next = child;
+			last = child;
 		}
 
-		if (result + len > der_len)
-			return FALSE;
-		result += len;
+	/* Non-structured TLV, just a value */
+	} else {
+		tlv->value = g_bytes_new_with_free_func (*at, len,
+		                                         (GDestroyNotify)g_bytes_unref,
+		                                         g_bytes_ref (data));
+		(*at) += len;
 	}
 
-	if (result > der_len)
-		return FALSE;
-	*rlen = result;
-	return TRUE;
-}
+	/* Note the actual DER that we decoded */
+	tlv->decoded = g_bytes_new_with_free_func (beg, *at - beg,
+	                                           (GDestroyNotify)g_bytes_unref,
+	                                           g_bytes_ref (data));
 
-static gboolean
-anode_decode_tlv_for_data (const guchar *data, const guchar *end, Atlv *tlv)
-{
-	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;
+	return NULL; /* Success */
 }
 
-static gboolean
-anode_decode_tlv_for_contents (Atlv *outer, gboolean first, Atlv *tlv)
+static const gchar *
+atlv_parse_der (GBytes *data,
+                Atlv *tlv)
 {
-	const guchar *data;
 	const guchar *end;
+	const guchar *at;
+	const gchar *ret;
+	guchar cls;
+	gulong tag;
+	gint off;
+	gint len;
+	gsize size;
 
-	if (first) {
-		data = outer->buf + outer->off;
-		end = outer->end;
-	} else {
-		data = tlv->end;
-		end = outer->end;
-	}
+	at = g_bytes_get_data (data, &size);
+	g_return_val_if_fail (at != NULL, FALSE);
+	end = at + size;
 
-	/* 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;
-	}
+	if (!atlv_parse_cls_tag_len (at, end, &cls, &tag, &off, &len))
+		return "content is not encoded properly";
 
-	g_return_val_if_fail (end > data, FALSE);
-	if (!anode_decode_tlv_for_data (data, end, tlv))
-		return FALSE;
+	ret = atlv_parse_der_tag (cls, tag, off, len, data, &at, tlv);
+	if (ret != NULL)
+		return ret;
 
-	/* 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;
-	}
+	if (at != end)
+		return "extra unexpected trailing data";
 
-	return TRUE;
+	return NULL; /* Success */
 }
 
+/* -------------------------------------------------------------------------
+ * DECODING
+ */
+
 static gboolean
 anode_decode_choice (GNode *node,
-                     GBytes *backing,
                      Atlv *tlv)
 {
 	gboolean have = FALSE;
@@ -880,7 +914,7 @@ anode_decode_choice (GNode *node,
 
 	for (child = node->children; child; child = child->next) {
 		an = (Anode*)child->data;
-		if (!have && anode_decode_anything (child, backing, tlv)) {
+		if (anode_decode_one (child, tlv)) {
 			an->chosen = 1;
 			have = TRUE;
 		} else {
@@ -895,82 +929,40 @@ anode_decode_choice (GNode *node,
 }
 
 static gboolean
-anode_decode_struct_string (GNode *node, Atlv *outer)
-{
-	gint i = 0;
-	Atlv tlv;
-
-	/* Recalculated below */
-	outer->len = 0;
-
-	for (i = 0; TRUE; ++i) {
-		if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv))
-			return anode_failure (node, "invalid encoding of child");
-		if (tlv.tag != outer->tag)
-			return anode_failure (node, "contents have an invalid tag");
-		outer->len = (tlv.end - outer->buf) - outer->off;
-	}
-
-	g_assert (outer->len >= 0);
-	return TRUE;
-}
-
-static gboolean
-anode_decode_struct_any (GNode *node, Atlv *tlv)
-{
-	if (tlv->len < 0) {
-		if (!anode_decode_indefinite_len (tlv->buf + tlv->off, tlv->end, &tlv->len))
-			return anode_failure (node, "could not find end of encoding");
-		tlv->end = tlv->buf + tlv->off + tlv->len;
-	}
-
-	return TRUE;
-}
-
-static gboolean
 anode_decode_sequence_or_set (GNode *node,
-                              GBytes *backing,
-                              Atlv *outer)
+                              Atlv *tlv)
 {
-	GNode *child;
-	Atlv tlv;
+	Atlv *ctlv;
+	gulong tag;
 	gint i;
 
-	/* Recalculated below */
-	outer->len = 0;
-
 	/*
 	 * 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 = node->children, i = 0; child; child = child->next, ++i) {
-
-		if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv))
-			return anode_failure (node, "invalid encoding of child");
-
-		if (!anode_decode_anything (child, backing, &tlv))
-			return FALSE;
-
-		outer->len = (tlv.end - outer->buf) - outer->off;
+	/* Tags must be in ascending order */
+	if (anode_def_type (node) == EGG_ASN1X_SET) {
+		for (ctlv = tlv->child, i = 0; ctlv != NULL; ctlv = ctlv->next, i++) {
+			if (i > 0 && tag > ctlv->tag)
+				return anode_failure (node, "content must be in ascending order");
+			tag = ctlv->tag;
+		}
 	}
 
-	g_assert (outer->len >= 0);
-	return TRUE;
+	return anode_decode_anything (node->children, tlv->child);
 }
 
 static gboolean
 anode_decode_sequence_or_set_of (GNode *node,
-                                 GBytes *backing,
-                                 Atlv *outer)
+                                 Atlv *tlv)
 {
+	Atlv *ctlv;
 	GNode *child, *other;
-	Atlv tlv;
+	gulong tag;
 	gint i;
 
-	outer->len = 0;
-
 	/* The first child */
 	child = node->children;
 	g_return_val_if_fail (child, FALSE);
@@ -979,15 +971,15 @@ anode_decode_sequence_or_set_of (GNode *node,
 	while (child->next)
 		anode_destroy (child->next);
 
-	/* Try to dig out as many of them as possible */
-	for (i = 0; TRUE; ++i) {
+	for (ctlv = tlv->child, i = 0; ctlv != NULL; ctlv = ctlv->next, i++) {
 
-		if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv))
-			return anode_failure (node, "invalid encoding of child");
+		/* Tag must have same tag as top */
+		if (i == 0)
+			tag = anode_calc_tag (child);
+		else if (tag != G_MAXULONG && ctlv->tag != tag)
+			return anode_failure (node, "invalid mismatched content");
 
-		/* The end of the road for us */
-		if (tlv.off == 0)
-			break;
+		/* TODO: Set of must be in ascending order in DER encoding */
 
 		if (i == 0) {
 			other = child;
@@ -996,171 +988,154 @@ anode_decode_sequence_or_set_of (GNode *node,
 			g_node_append (node, other);
 		}
 
-		if (!anode_decode_anything (other, backing, &tlv))
+		if (!anode_decode_one (other, ctlv))
 			return FALSE;
-
-		outer->len = (tlv.end - outer->buf) - outer->off;
 	}
 
-	g_assert (outer->len >= 0);
+	return TRUE;
+}
+
+static gboolean
+anode_decode_bit_string (GNode *node,
+                         Atlv *tlv)
+{
+	Anode *an = node->data;
+	guchar empty, mask;
+	GBytes *value;
+	const guchar *buf;
+	gsize len;
+
+	buf = g_bytes_get_data (tlv->value, &len);
+	if (len == 0)
+		return anode_failure (node, "invalid length bit string");
+
+	/* The first byte is the number of empty bits */
+	empty = buf[0];
+	if (empty >= 8)
+		return anode_failure (node, "invalid number of empty bits");
+
+	/* Free bits at end must be zero */
+	mask = 0xFF >> (8 - empty);
+	if (len > 1 && buf[len - 1] & mask)
+		return anode_failure (node, "bit string has invalid trailing bits");
+
+	value = g_bytes_new_from_bytes (tlv->value, 1, len - 1);
+	anode_take_value (node, value);
+	an = node->data;
+	an->bits_empty = empty;
 	return TRUE;
 }
 
 static gboolean
 anode_decode_primitive (GNode *node,
-                        GBytes *backing,
                         Atlv *tlv,
                         gint flags)
 {
-	gint type;
+	/* Must not have any tlv children */
+	g_assert (tlv->child == NULL);
 
-	/* Must have a definite length */
-	if (tlv->len < 0)
-		return anode_failure (node, "primitive value with an indefinite length");
+	switch (anode_def_type (node)) {
 
-	type = anode_def_type (node);
-	switch (type) {
+	/* Handle bit strings specially */
+	case EGG_ASN1X_BIT_STRING:
+		return anode_decode_bit_string (node, tlv);
 
 	/* The primitive value types */
 	case EGG_ASN1X_INTEGER:
 	case EGG_ASN1X_ENUMERATED:
 	case EGG_ASN1X_BOOLEAN:
-	case EGG_ASN1X_BIT_STRING:
 	case EGG_ASN1X_OCTET_STRING:
 	case EGG_ASN1X_OBJECT_ID:
 	case EGG_ASN1X_NULL:
 	case EGG_ASN1X_GENERALSTRING:
 	case EGG_ASN1X_TIME:
-		anode_set_tlv_data (node, backing, tlv);
+		anode_set_value (node, tlv->value);
 		return TRUE;
 
-	/* Transparent types */
+	/* Just use the 'parsed' which is automatically set */
 	case EGG_ASN1X_ANY:
-		anode_set_tlv_data (node, backing, tlv);
 		return TRUE;
 
 	case EGG_ASN1X_CHOICE:
-		if (!anode_decode_choice (node, backing, tlv))
-			return FALSE;
-		anode_set_tlv_data (node, backing, tlv);
-		return TRUE;
+		return anode_decode_choice (node, tlv);
 
 	default:
 		return anode_failure (node, "primitive value of an unexpected type");
 	}
-
-	g_assert_not_reached ();
 }
 
 static gboolean
 anode_decode_structured (GNode *node,
-                         GBytes *backing,
                          Atlv *tlv,
                          gint flags)
 {
-	gboolean definite;
-	const guchar *end;
-	Atlv ctlv;
-	gint len;
-	gulong tag;
-	guchar cls;
-	gint off = 0;
+	switch (anode_def_type (node)) {
+
+	/* Just use the 'parsed' which is automatically set */
+	case EGG_ASN1X_ANY:
+	case EGG_ASN1X_GENERALSTRING:
+	case EGG_ASN1X_OCTET_STRING:
+		return TRUE;
+
+	case EGG_ASN1X_CHOICE:
+		return anode_decode_choice (node, tlv);
+
+	case EGG_ASN1X_SEQUENCE:
+	case EGG_ASN1X_SET:
+		return anode_decode_sequence_or_set (node, tlv);
+
+	case EGG_ASN1X_SEQUENCE_OF:
+	case EGG_ASN1X_SET_OF:
+		return anode_decode_sequence_or_set_of (node, tlv);
+
+	default:
+		return anode_failure (node, "structured value of an unexpected type");
+	}
+}
 
-	definite = (tlv->len >= 0);
-	end = tlv->end;
+static gboolean
+anode_decode_one_without_tag (GNode *node,
+                              Atlv *tlv,
+                              gint flags)
+{
+	gboolean ret;
+	Anode *an;
 
 	/* An explicit, wrapped tag */
 	if (anode_calc_explicit_for_flags (node, flags, NULL)) {
 		if ((tlv->cls & ASN1_CLASS_CONTEXT_SPECIFIC) == 0)
 			return anode_failure (node, "missing context specific tag");
-		if (!anode_decode_tlv_for_contents (tlv, TRUE, &ctlv))
-			return anode_failure (node, "invalid encoding of child");
+		if (tlv->child == NULL)
+			return anode_failure (node, "missing context specific child");
+		if (tlv->child->next != NULL)
+			return anode_failure (node, "multiple context specific children");
 		flags &= ~FLAG_TAG;
-		if (!anode_decode_anything_for_flags (node, backing, &ctlv, flags))
-			return FALSE;
+		ret = anode_decode_one_without_tag (node, tlv->child, flags);
 
-		/* Use most of the child's tlv */
-		tlv->cls = ctlv.cls;
-		tlv->tag = ctlv.tag;
-		tlv->off += ctlv.off;
-		tlv->oft = ctlv.off;
-		tlv->len = ctlv.len;
-		anode_clr_tlv_data (node);
+	/* Structured value */
+	} else if (tlv->cls & ASN1_CLASS_STRUCTURED) {
+		ret = anode_decode_structured (node, tlv, flags);
 
-	/* Other structured types */
+	/* A primitive simple value */
 	} else {
-		switch (anode_def_type (node)) {
-		case EGG_ASN1X_ANY:
-			if (!anode_decode_struct_any (node, tlv))
-				return FALSE;
-			break;
-		case EGG_ASN1X_CHOICE:
-			if (!anode_decode_choice (node, backing, tlv))
-				return FALSE;
-			break;
-		case EGG_ASN1X_GENERALSTRING:
-		case EGG_ASN1X_OCTET_STRING:
-			if (!anode_decode_struct_string (node, tlv))
-				return FALSE;
-			break;
-		case EGG_ASN1X_SEQUENCE:
-		case EGG_ASN1X_SET:
-			if (!anode_decode_sequence_or_set (node, backing, tlv))
-				return FALSE;
-			break;
-		case EGG_ASN1X_SEQUENCE_OF:
-		case EGG_ASN1X_SET_OF:
-			if (!anode_decode_sequence_or_set_of (node, backing, tlv))
-				return FALSE;
-			break;
-		default:
-			return FALSE;
-		}
-	}
-
-	g_return_val_if_fail (tlv->len >= 0, FALSE);
-
-	/* 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 anode_failure (node, "end of indefinite content is missing");
-		if (!anode_check_indefinite_end (cls, tag, len))
-			return anode_failure (node, "end of indefinite content is invalid");
-		end = tlv->buf + tlv->off + tlv->len + off;
+		ret = anode_decode_primitive (node, tlv, flags);
 	}
 
-	/* A structure must be filled up, no stuff ignored */
-	if (tlv->buf + tlv->off + tlv->len + off < end)
-		return anode_failure (node, "extra data at the end of the content");
-	g_return_val_if_fail (tlv->buf + tlv->off + tlv->len + off == end, FALSE);
-
-	tlv->end = end;
-	anode_set_tlv_data (node, backing, tlv);
-	return TRUE;
-}
-
-static gboolean
-anode_decode_option_or_default (GNode *node, Atlv *tlv, gint flags)
-{
-	if (flags & FLAG_OPTION || flags & FLAG_DEFAULT) {
-		tlv->len = 0;
-		tlv->end = tlv->buf;
-		tlv->off = 0;
-		anode_clr_tlv_data (node);
-		return TRUE;
+	/* Mark which tlv we used for this node */
+	if (ret) {
+		an = node->data;
+		atlv_free (an->parsed);
+		an->parsed = atlv_dup (tlv, FALSE);
 	}
 
-	return FALSE;
+	return ret;
 }
 
 static gboolean
-anode_decode_anything_for_flags (GNode *node,
-                                 GBytes *bytes,
-                                 Atlv *tlv,
-                                 gint flags)
+anode_decode_one (GNode *node,
+                  Atlv *tlv)
 {
-	gboolean ret;
+	gint flags = anode_def_flags (node);
 	gulong tag;
 
 	tag = anode_calc_tag_for_flags (node, flags);
@@ -1169,87 +1144,130 @@ anode_decode_anything_for_flags (GNode *node,
 	if (tag == G_MAXULONG)
 		tag = tlv->tag;
 
-	/* Tag does not match, what do we do? */
-	if (tlv->off == 0 || tag != tlv->tag) {
-		if (anode_decode_option_or_default (node, tlv, flags))
-			return TRUE;
+	/* We have no match */
+	if (tag != tlv->tag)
 		return anode_failure (node, "decoded tag did not match expected");
-	}
 
-	/* Structured value */
-	if (tlv->cls & ASN1_CLASS_STRUCTURED)
-		ret = anode_decode_structured (node, bytes, tlv, flags);
+	return anode_decode_one_without_tag (node, tlv, flags);
+}
 
-	/* A primitive simple value */
-	else
-		ret = anode_decode_primitive (node, bytes, tlv, flags);
+static gboolean
+anode_decode_option_or_default (GNode *node)
+{
+	gint flags = anode_def_flags (node);
 
-	return ret;
+	if (flags & FLAG_OPTION || flags & FLAG_DEFAULT) {
+		anode_clr_value (node);
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 static gboolean
 anode_decode_anything (GNode *node,
-                       GBytes *bytes,
                        Atlv *tlv)
 {
-	gint flags = anode_def_flags (node);
+	GNode *next;
+	gulong tag;
+	gint flags;
 
-	if (!anode_decode_anything_for_flags (node, bytes, tlv, flags))
-		return anode_decode_option_or_default (node, tlv, flags);
+	while (tlv != NULL) {
+		flags = anode_def_flags (node);
+		tag = anode_calc_tag_for_flags (node, flags);
+
+		/* We don't know what the tag is supposed to be */
+		if (tag == G_MAXULONG)
+			tag = tlv->tag;
+
+		/* We have no match */
+		if (tag != tlv->tag) {
+
+			/* See if we can skip this node */
+			if (anode_decode_option_or_default (node))
+				next = g_node_next_sibling (node);
+			else
+				next = NULL;
+
+			if (next == NULL)
+				return anode_failure (node, "decoded tag did not match expected");
+
+			node = next;
+			continue;
+		}
+
+		if (!anode_decode_one_without_tag (node, tlv, flags))
+			return FALSE;
+
+		/* Next node and tag */
+		node = g_node_next_sibling (node);
+		tlv = tlv->next;
+	}
+
+	/* We have no values for these nodes */
+	while (node != NULL) {
+		if (anode_decode_option_or_default (node))
+			node = g_node_next_sibling (node);
+		else
+			return anode_failure (node, "no decoded value");
+	}
 
 	return TRUE;
 }
 
 gboolean
-egg_asn1x_decode_no_validate (GNode *asn,
-                              GBytes *data)
+egg_asn1x_decode_full (GNode *asn,
+                       GBytes *data,
+                       gint options)
 {
-	const guchar *dat;
-	gsize size;
-	Atlv tlv;
+	const gchar *msg;
+	gboolean ret;
+	Anode *an;
+	Atlv *tlv;
 
 	g_return_val_if_fail (asn != NULL, FALSE);
 	g_return_val_if_fail (data != NULL, FALSE);
 
 	egg_asn1x_clear (asn);
 
-	dat = g_bytes_get_data (data, &size);
-	g_return_val_if_fail (dat != NULL, FALSE);
-
-	if (!anode_decode_tlv_for_data (dat, dat + size, &tlv))
-		return anode_failure (asn, "content is not encoded properly");
+	tlv = atlv_new ();
+	msg = atlv_parse_der (data, tlv);
+	if (msg == NULL) {
+		ret = anode_decode_anything (asn, tlv);
 
-	if (!anode_decode_anything (asn, data, &tlv))
-		return FALSE;
+	/* A failure, set the message manually so it doesn't get a prefix */
+	} else {
+		an = asn->data;
+		g_free (an->failure);
+		an->failure = g_strdup (msg);
+		ret = FALSE;
+	}
 
-	if (tlv.end - tlv.buf != size)
+	atlv_free (tlv);
+	if (ret == FALSE)
 		return FALSE;
 
-	return TRUE;
+	return egg_asn1x_validate (asn, !(options & EGG_ASN1X_NO_STRICT));
 }
 
 gboolean
 egg_asn1x_decode (GNode *asn,
                   GBytes *data)
 {
-	gboolean ret;
-
 	g_return_val_if_fail (asn != NULL, FALSE);
 	g_return_val_if_fail (data != NULL, FALSE);
 
-	ret = egg_asn1x_decode_no_validate (asn, data);
-	if (!ret)
-		return ret;
-
-	return egg_asn1x_validate (asn, TRUE);
+	return egg_asn1x_decode_full (asn, data, 0);
 }
 
 /* -----------------------------------------------------------------------------------
- * ENCODING
+ * UNPARSE
  */
 
 static void
-anode_encode_length (gulong len, guchar *ans, gint *cb)
+atlv_unparse_len (gulong len,
+                  guchar *ans,
+                  gint *cb)
 {
 	guchar temp[sizeof (gulong)];
 	gint k;
@@ -1279,8 +1297,11 @@ anode_encode_length (gulong len, guchar *ans, gint *cb)
 }
 
 static gint
-anode_encode_cls_tag_len (guchar *data, gsize n_data, guchar cls,
-                          gulong tag, gint len)
+atlv_unparse_cls_tag_len (guchar *data,
+                          gsize n_data,
+                          guchar cls,
+                          gulong tag,
+                          gint len)
 {
 	guchar temp[sizeof(gulong)];
 	gint cb;
@@ -1313,7 +1334,7 @@ anode_encode_cls_tag_len (guchar *data, gsize n_data, guchar cls,
 
 	/* And now the length */
 	cb = n_data - off;
-	anode_encode_length (len, data ? data + off : NULL, &cb);
+	atlv_unparse_len (len, data ? data + off : NULL, &cb);
 	off += cb;
 
 	g_assert (!data || n_data >= off);
@@ -1321,422 +1342,311 @@ anode_encode_cls_tag_len (guchar *data, gsize n_data, guchar cls,
 }
 
 static void
-anode_encode_tlv_and_enc (GNode *node,
-                          gsize n_data,
-                          Aencoder encoder,
-                          gpointer user_data,
-                          GDestroyNotify destroy)
+atlv_unparse_der (Atlv *tlv,
+                  guchar **at,
+                  guchar *end)
 {
-	gboolean explicit = FALSE;
-	guchar cls_type;
-	gulong tag;
-	gint flags;
-	Atlv tlv;
-
-	g_assert (node);
-	g_assert (encoder);
-
-	/* The data length */
-	memset (&tlv, 0, sizeof (tlv));
-	tlv.len = n_data;
-
-	/* Figure out the basis if the class */
-	switch (anode_def_type (node)) {
-	case EGG_ASN1X_INTEGER:
-	case EGG_ASN1X_BOOLEAN:
-	case EGG_ASN1X_BIT_STRING:
-	case EGG_ASN1X_OCTET_STRING:
-	case EGG_ASN1X_OBJECT_ID:
-	case EGG_ASN1X_TIME:
-	case EGG_ASN1X_ENUMERATED:
-	case EGG_ASN1X_GENERALSTRING:
-	case EGG_ASN1X_NULL:
-		tlv.cls = ASN1_CLASS_UNIVERSAL;
-		break;
-	/* Container types */
-	case EGG_ASN1X_SEQUENCE:
-	case EGG_ASN1X_SET:
-	case EGG_ASN1X_SEQUENCE_OF:
-	case EGG_ASN1X_SET_OF:
-		tlv.cls = (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL);
-		break;
+	const guchar *exp;
+	const guchar *buf;
+	guchar *p;
+	guchar mask;
+	Atlv *ctlv;
+	gint off;
+	gsize len;
 
-	/* Transparent types shouldn't get here */
-	case EGG_ASN1X_ANY:
-	case EGG_ASN1X_CHOICE:
-		g_return_if_reached ();
+	g_assert (*at <= end);
 
-	default:
-		g_return_if_reached ();
-	};
+	off = atlv_unparse_cls_tag_len (*at, end - *at, tlv->cls,
+	                                tlv->tag, tlv->len);
+	g_assert (off == tlv->off);
+	(*at) += off;
+
+	/* Write a value */
+	if (tlv->value) {
+		buf = g_bytes_get_data (tlv->value, &len);
+		p = *at;
+
+		/* Special behavior for bit strings */
+		if (tlv->prefix_for_bit_string) {
+			g_assert (len + 1 == tlv->len);
+			p[0] = (guchar)tlv->bits_empty;
+			memcpy (p + 1, buf, len);
+
+			/* Set the extra bits to zero */
+			if (len && tlv->bits_empty) {
+				mask = 0xFF >> (8 - tlv->bits_empty);
+				p[len] &= ~mask;
+			}
+			p += len + 1;
 
-	/* Build up the class */
-	flags = anode_def_flags (node);
-	if (flags & FLAG_TAG) {
-		explicit = anode_calc_explicit_for_flags (node, flags, &cls_type);
-		if (explicit)
-			flags &= ~FLAG_TAG;
-		else
-			tlv.cls |= cls_type;
-	}
+		/* Special behavior for prefixed integers */
+		} else if (tlv->prefix_with_zero_byte) {
+			g_assert (len + 1 == tlv->len);
+			p[0] = 0;
+			memcpy (p + 1, buf, len);
+			p += len + 1;
 
-	/* And now the tag */
-	tlv.tag = anode_calc_tag_for_flags (node, flags);
+		/* Standard behavior */
+		} else {
+			g_assert (len == tlv->len);
+			memcpy (p, buf, len);
+			p += len;
+		}
 
-	/* Calculate the length for the main thingy */
-	tlv.off = anode_encode_cls_tag_len (NULL, 0, tlv.cls, tlv.tag, tlv.len);
+		*at = p;
 
-	/* Wrap that in another explicit tag if necessary */
-	if (explicit) {
-		tag = anode_calc_tag (node);
-		g_return_if_fail (tag != G_MAXULONG);
-		tlv.oft = anode_encode_cls_tag_len (NULL, 0, 0, tag, tlv.off + tlv.len);
-		tlv.off += tlv.oft;
+	/* Write a bunch of child TLV's */
+	} else {
+		for (ctlv = tlv->child; ctlv != NULL; ctlv = ctlv->next) {
+			exp = *at + ctlv->len + ctlv->off;
+			atlv_unparse_der (ctlv, at, end);
+			g_assert (exp == *at);
+		}
 	}
 
-	/* Not completely filled in */
-	tlv.buf = tlv.end = NULL;
-
-	anode_clear (node);
-	anode_set_tlv_data (node, NULL, &tlv);
-	anode_set_enc_data (node, encoder, user_data, destroy);
+	g_assert (*at <= end);
 }
 
-static gboolean
-anode_encode_build (GNode *node,
-                    GBytes *backing,
-                    guchar *data,
-                    gsize n_data)
+static GBytes *
+atlv_unparse_to_bytes (Atlv *tlv,
+                       EggAllocator allocator)
 {
-	guchar cls_type;
-	gint type;
-	guchar cls;
-	gulong tag;
-	Aenc *enc;
-	Atlv *tlv;
-	gint off = 0;
-
-	type = anode_def_type (node);
-	tlv = anode_get_tlv_data (node);
-	g_return_val_if_fail (tlv, FALSE);
-
-	/* Should have an encoder */
-	enc = anode_get_enc_data (node);
-	g_return_val_if_fail (enc, FALSE);
-
-	/* If it's a choice node, use the choice for calculations */
-	if (type == EGG_ASN1X_CHOICE) {
-		node = egg_asn1x_get_choice (node);
-		g_return_val_if_fail (node, FALSE);
-	}
-
-	/* Encode any explicit tag */
-	if (anode_calc_explicit (node, &cls_type)) {
-		tag = anode_calc_tag (node);
-		g_return_val_if_fail (tag != G_MAXULONG, FALSE);
-		cls = (ASN1_CLASS_STRUCTURED | cls_type);
-		g_assert (tlv->oft > 0 && tlv->oft < tlv->off);
-		off = anode_encode_cls_tag_len (data, n_data, cls, tag, (tlv->off - tlv->oft) + tlv->len);
-		g_assert (off == tlv->oft);
-	}
+	GBytes *bytes;
+	guchar *data;
+	guchar *at;
+	gint len;
 
-	/* Now encode the main tag */
-	off += anode_encode_cls_tag_len (data + off, n_data - off, tlv->cls, tlv->tag, tlv->len);
-	g_assert (off == tlv->off);
+	/* Allocate enough memory for entire thingy */
+	len = tlv->off + tlv->len;
+	g_return_val_if_fail (len != 0, NULL);
 
-	/* Setup the remainder of the tlv */
-	g_assert (tlv->len + tlv->off == n_data);
-	tlv->buf = data;
-	tlv->end = data + n_data;
-	anode_set_backing (node, backing);
+	bytes = bytes_new_with_allocator (allocator, &data, len);
+	if (data == NULL)
+		return NULL;
 
-	/* Encode in the data */
-	if (!(enc->encoder) (enc->data, node, data + tlv->off, tlv->len))
-		return FALSE;
+	at = data;
+	atlv_unparse_der (tlv, &at, data + len);
+	g_assert (at == data + len);
 
-	return TRUE;
+	return bytes;
 }
 
-static void
-anode_encode_rollback (GNode *node)
-{
-	GNode *child;
-	Aenc *enc;
+typedef struct {
+	GBytes *bytes;
 	Atlv *tlv;
-
-	/* Undo any references to our new buffer */
-	enc = anode_get_enc_data (node);
-	if (enc) {
-		tlv = anode_get_tlv_data (node);
-		g_return_if_fail (tlv);
-		tlv->buf = tlv->end = NULL;
-	}
-
-	for (child = node->children; child; child = child->next)
-		anode_encode_rollback (child);
-}
-
-static void
-anode_encode_commit (GNode *node)
-{
-	GNode *child;
-
-	/* Remove and free all the encoder stuff */
-	anode_clr_enc_data (node);
-
-	for (child = node->children; child; child = child->next)
-		anode_encode_commit (child);
-}
+} SortPair;
 
 static gint
-compare_bufs (gconstpointer a, gconstpointer b)
+compare_sort_pair (gconstpointer a,
+                   gconstpointer b)
 {
-	const Abuf *ba = a;
-	const Abuf *bb = b;
-	gint res = memcmp (ba->data, bb->data, MIN (ba->n_data, bb->n_data));
-	if (ba->n_data == bb->n_data || res != 0)
-		return res;
-	return ba->n_data < bb->n_data ? -1 : 1;
+	const SortPair *sa = a;
+	const SortPair *sb = b;
+	return g_bytes_compare (sa->bytes, sb->bytes);
 }
 
-static gboolean
-traverse_and_sort_set_of (GNode *node, gpointer user_data)
+static void
+atlv_sort_perform (Atlv *tlv,
+                   EggAllocator allocator)
 {
-	EggAllocator allocator = user_data;
-	GList *bufs, *l;
-	Abuf *buf;
-	guchar *data;
-	gsize n_data;
-	Atlv *tlv;
-	GNode *child;
-	GNode *next;
-
-	if (!allocator)
-		allocator = g_realloc;
-
-	/* We have to sort any SET OF :( */
-	if (anode_def_type (node) != EGG_ASN1X_SET_OF)
-		return FALSE;
+	GList *pairs, *l;
+	SortPair *pair;
+	GBytes *bytes;
+	Atlv *ctlv;
+	Atlv *last;
+	gboolean sort;
 
-	bufs = NULL;
-	for (child = node->children; child; child = next) {
-		next = child->next;
+	for (ctlv = tlv->child; ctlv != NULL; ctlv = ctlv->next)
+		atlv_sort_perform (ctlv, allocator);
 
-		tlv = anode_get_tlv_data (child);
-		if (!tlv)
-			continue;
+	if (!tlv->sorted)
+		return;
 
-		/* Allocate enough memory */
-		n_data = tlv->len + tlv->off;
-		data = (allocator) (NULL, n_data + 1);
-		if (!data)
+	pairs = NULL;
+	for (ctlv = tlv->child; ctlv != NULL; ctlv = ctlv->next) {
+		bytes = atlv_unparse_to_bytes (ctlv, allocator);
+		if (bytes == NULL)
 			break;
 
-		if (!anode_encode_build (child, NULL, data, n_data)) {
-			(allocator) (data, 0);
-			continue;
-		}
-
-		buf = g_slice_new0 (Abuf);
-		buf->user_data = child;
-		buf->n_data = n_data;
-		buf->data = data;
-		bufs = g_list_prepend (bufs, buf);
-		g_node_unlink (child);
+		pair = g_slice_new0 (SortPair);
+		pair->bytes = bytes;
+		pair->tlv = ctlv;
+		pairs = g_list_prepend (pairs, pair);
 	}
 
-	bufs = g_list_sort (bufs, compare_bufs);
+	/* Only sort of the above unparse completed for all */
+	sort = ctlv == NULL;
+	last = NULL;
+
+	pairs = g_list_sort (pairs, compare_sort_pair);
+	for (l = pairs; l != NULL; l = g_list_next (l)) {
+		pair = l->data;
+
+		/* Only if the sort completed */
+		if (sort) {
+			if (last == NULL)
+				tlv->child = pair->tlv;
+			else
+				last->next = pair->tlv;
+			last = pair->tlv;
+		}
 
-	for (l = bufs; l; l = g_list_next (l)) {
-		buf = l->data;
-		g_node_append (node, buf->user_data);
-		(allocator) (buf->data, 0);
-		g_slice_free (Abuf, buf);
-	}
+		g_bytes_unref (pair->bytes);
+		g_slice_free (SortPair, pair);
+ 	}
 
-	anode_encode_rollback (node);
-	g_list_free (bufs);
-	return FALSE;
+	g_list_free (pairs);
 }
 
-static gboolean
-anode_encoder_bytes (gpointer user_data,
-                     GNode *node,
-                     guchar *data,
-                     gsize n_data)
+static void
+anode_build_cls_tag_len (GNode *node,
+                         Atlv *tlv,
+                         gint len)
 {
-	GBytes *bytes = user_data;
-	g_assert (g_bytes_get_size (bytes) >= n_data);
-	memcpy (data, g_bytes_get_data (bytes, NULL), n_data);
-	return TRUE;
-}
+	gboolean explicit = FALSE;
+	guchar cls_type;
+	gint flags;
 
-static gboolean
-anode_encoder_data (gpointer user_data,
-                    GNode *node,
-                    guchar *data,
-                    gsize n_data)
-{
-	memcpy (data, user_data, n_data);
-	return TRUE;
-}
+	/* One for the prefix character */
+	if (tlv->prefix_for_bit_string ||
+	    tlv->prefix_with_zero_byte)
+		len += 1;
 
-static gboolean
-anode_encoder_unsigned (gpointer user_data,
-                        GNode *node,
-                        guchar *data,
-                        gsize n_data)
-{
-	GBytes *value = user_data;
-	gboolean sign;
-	const gchar *p;
+	/* Figure out the basis if the class */
+	switch (anode_def_type (node)) {
+	case EGG_ASN1X_INTEGER:
+	case EGG_ASN1X_BOOLEAN:
+	case EGG_ASN1X_BIT_STRING:
+	case EGG_ASN1X_OCTET_STRING:
+	case EGG_ASN1X_OBJECT_ID:
+	case EGG_ASN1X_TIME:
+	case EGG_ASN1X_ENUMERATED:
+	case EGG_ASN1X_GENERALSTRING:
+	case EGG_ASN1X_NULL:
+		tlv->cls = ASN1_CLASS_UNIVERSAL;
+		break;
+	/* Container types */
+	case EGG_ASN1X_SEQUENCE:
+	case EGG_ASN1X_SET:
+	case EGG_ASN1X_SEQUENCE_OF:
+	case EGG_ASN1X_SET_OF:
+		tlv->cls = (ASN1_CLASS_STRUCTURED | ASN1_CLASS_UNIVERSAL);
+		break;
 
-	/*
-	 * If top bit is set, the result would be negative in two's complement
-	 * but since we want an unsigned integer, add a zero byte. That zero
-	 * byte is already calculated into n_data, see egg_asn1x_set_integer_as_usg
-	 */
+	/* Transparent types shouldn't get here */
+	case EGG_ASN1X_ANY:
+	case EGG_ASN1X_CHOICE:
+	default:
+		g_assert_not_reached ();
+	};
 
-	p = g_bytes_get_data (value, NULL);
-	g_return_val_if_fail (p != NULL, FALSE);
+	flags = anode_def_flags (node);
 
-	sign = !!(p[0] & 0x80);
-	if (sign) {
-		g_assert (n_data > 1);
-		data[0] = 0;
-		data++;
-		n_data--;
+	/* Build up the class */
+	if (flags & FLAG_TAG) {
+		explicit = anode_calc_explicit_for_flags (node, flags, &cls_type);
+		if (explicit)
+			flags &= ~FLAG_TAG;
+		else
+			tlv->cls |= cls_type;
 	}
 
-	memcpy (data, p, n_data);
-	return TRUE;
+	/* Setup the class */
+	tlv->tag = anode_calc_tag_for_flags (node, flags);
+
+	/* The offset and length */
+	tlv->len = len;
+	tlv->off = atlv_unparse_cls_tag_len (NULL, 0, tlv->cls, tlv->tag, len);
 }
 
-static gboolean
-anode_encoder_structured (gpointer user_data,
-                          GNode *unused,
-                          guchar *data,
-                          gsize n_data)
+static Atlv *
+anode_build_value (GNode *node)
 {
-	GNode *node = user_data;
-	GNode *child;
-	gsize length;
+	Anode *an = node->data;
 	Atlv *tlv;
+	gsize len;
 
-	for (child = node->children; child; child = child->next) {
-		tlv = anode_get_tlv_data (child);
-		if (tlv) {
-			length = tlv->off + tlv->len;
-			g_assert (length <= n_data);
-			if (!anode_encode_build (child, anode_get_backing (node),
-			                         data, length))
-				return FALSE;
-			data += length;
-			n_data -= length;
-		}
-	}
+	/* Fill this in based on the value */
+	if (an->value == NULL)
+		return NULL;
 
-	return TRUE;
+	tlv = atlv_new ();
+	tlv->value = g_bytes_ref (an->value);
+
+	len = g_bytes_get_size (an->value);
+	anode_build_cls_tag_len (node, tlv, len);
+	return tlv;
 }
 
-static gboolean
-anode_encoder_choice (gpointer user_data,
-                      GNode *unused,
-                      guchar *data,
-                      gsize n_data)
+static Atlv *
+anode_build_bit_string (GNode *node)
 {
-	GNode *node = user_data;
-	Aenc *enc = NULL;
-	GNode *child;
-	Atlv *tlv, *ctlv;
-
-	tlv = anode_get_tlv_data (node);
-	g_return_val_if_fail (tlv, FALSE);
-
-	child = egg_asn1x_get_choice (node);
-	g_return_val_if_fail (child, FALSE);
-
-	ctlv = anode_get_tlv_data (child);
-	g_assert (ctlv);
+	Anode *an = node->data;
+	Atlv *tlv;
+	gsize len;
 
-	enc = anode_get_enc_data (child);
-	g_return_val_if_fail (enc, FALSE);
-	if (!(enc->encoder) (enc->data, child, data, n_data))
-		return FALSE;
+	if (an->value == NULL)
+		return NULL;
 
-	/* Child's buffer matches ours */
-	ctlv->buf = tlv->buf;
-	ctlv->end = tlv->end;
+	tlv = atlv_new ();
+	tlv->value = g_bytes_ref (an->value);
+	tlv->bits_empty = an->bits_empty;
+	tlv->prefix_for_bit_string = 1;
 
-	return TRUE;
+	len = g_bytes_get_size (an->value);
+	anode_build_cls_tag_len (node, tlv, len);
+	return tlv;
 }
 
-static gboolean
-anode_encoder_bit_string (gpointer user_data,
-                          GNode *node,
-                          guchar *data,
-                          gsize n_data)
+static Atlv *
+anode_build_integer (GNode *node)
 {
-	Abits *ab = user_data;
-	guchar empty, mask;
+	Anode *an = node->data;
+	const guchar *buf;
+	gboolean sign;
 	gsize len;
+	Atlv *tlv;
 
-	empty = ab->n_bits % 8;
-	if (empty > 0)
-		empty = 8 - empty;
-	len = (ab->n_bits / 8) + (empty ? 1 : 0);
-	g_assert (n_data == len + 1);
+	if (an->value == NULL)
+		return NULL;
 
-	/* Fill in the amount of empty */
-	data[0] = empty;
-	data += 1;
+	tlv = atlv_new ();
+	tlv->value = g_bytes_ref (an->value);
 
-	/* Fill in the actual data */
-	memcpy (data, g_bytes_get_data (ab->bits, NULL), len);
+	buf = g_bytes_get_data (an->value, &len);
+	if (an->guarantee_unsigned) {
 
-	/* Set the extra bits to zero */
-	if (len && empty) {
-		mask = 0xFF >> (8 - empty);
-		data[len - 1] &= ~mask;
+		/*
+		 * In two's complement (which DER is) this would be negative, add a zero
+		 * byte so that it isn't. Here we just note that the result will be one
+		 * byte longer.
+		 */
+		sign = !!(buf[0] & 0x80);
+		if (sign)
+			tlv->prefix_with_zero_byte = 1;
 	}
 
-	return TRUE;
+	anode_build_cls_tag_len (node, tlv, len);
+	return tlv;
 }
 
-static gboolean
-anode_encode_prepare_simple (GNode *node, gboolean want)
+static Atlv *
+anode_build_any (GNode *node)
 {
-	GBytes *backing;
-	GBytes *bytes;
-	Aenc *enc;
-	Atlv *tlv;
+	Atlv *parsed;
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL)
-		return FALSE;
-
-	/* Transfer the tlv data over to enc */
-	enc = anode_get_enc_data (node);
-	if (enc == NULL) {
-		backing = anode_get_backing (node);
-		if (backing == NULL)
-			return FALSE;
+	/* Fill this in based on already parsed TLVs */
+	parsed = anode_get_parsed (node);
+	if (parsed != NULL)
+		return atlv_dup (parsed, FALSE);
 
-		bytes = g_bytes_new_with_free_func ((guchar *)tlv->buf + tlv->off, tlv->len,
-		                                    (GDestroyNotify)g_bytes_unref,
-		                                    g_bytes_ref (backing));
-		anode_set_enc_data (node, anode_encoder_bytes, bytes,
-		                    (GDestroyNotify)g_bytes_unref);
-	}
-
-	tlv->buf = tlv->end = NULL;
-	return TRUE;
+	return NULL;
 }
 
-static gboolean
-anode_encode_prepare_choice (GNode *node, gboolean want)
+static Atlv *
+anode_build_choice (GNode *node,
+                    gboolean want)
 {
-	Atlv *tlv;
 	GNode *child;
 
 	g_assert (anode_def_type (node) == EGG_ASN1X_CHOICE);
@@ -1745,127 +1655,143 @@ anode_encode_prepare_choice (GNode *node, gboolean want)
 	if (!child)
 		return FALSE;
 
-	if (!anode_encode_prepare (child, want))
-		return FALSE;
-
-	tlv = anode_get_tlv_data (child);
-	g_return_val_if_fail (tlv, FALSE);
-	anode_clr_tlv_data (node);
-	anode_set_tlv_data (node, NULL, tlv);
-	anode_set_enc_data (node, anode_encoder_choice, node, NULL);
-
-	return TRUE;
-
+	return anode_build_anything (child, want);
 }
 
-static gboolean
-anode_encode_prepare_structured (GNode *node, gboolean want)
+static Atlv *
+anode_build_structured (GNode *node,
+                        gboolean want)
 {
 	gboolean child_want;
-	gsize length;
-	gboolean had;
+	Atlv *last;
+	Atlv *ctlv;
 	Atlv *tlv;
 	GNode *child;
 	gint type;
+	gint len;
 
 	type = anode_def_type (node);
 	child_want = want;
-	had = FALSE;
-	length = 0;
+	last = NULL;
+	len = 0;
 
 	if (type == EGG_ASN1X_SEQUENCE_OF || type == EGG_ASN1X_SET_OF)
 		child_want = FALSE;
 	if (anode_def_flags (node) & FLAG_OPTION)
 		want = FALSE;
 
-	for (child = node->children; child; child = child->next) {
-		if (anode_encode_prepare (child, child_want)) {
-			tlv = anode_get_tlv_data (child);
-			g_return_val_if_fail (tlv, FALSE);
-			length += tlv->off + tlv->len;
-			had = TRUE;
+	tlv = atlv_new ();
+	for (child = node->children; child != NULL; child = child->next) {
+		ctlv = anode_build_anything (child, child_want);
+		if (ctlv != NULL) {
+			if (last == NULL)
+				tlv->child = ctlv;
+			else
+				last->next = ctlv;
+			last = ctlv;
+			len += ctlv->off + ctlv->len;
 		}
 	}
 
-	if (had == FALSE) {
+	if (last == NULL) {
 		/* See if we should encode an empty set or seq of */
 		if (type == EGG_ASN1X_SEQUENCE_OF || type == EGG_ASN1X_SET_OF) {
-			if (!want)
-				return FALSE;
+			if (!want) {
+				atlv_free (tlv);
+				return NULL;
+			}
 		} else {
-			return FALSE;
+			atlv_free (tlv);
+			return NULL;
 		}
 	}
 
-	anode_encode_tlv_and_enc (node, length, anode_encoder_structured, node, NULL);
-	return TRUE;
+	anode_build_cls_tag_len (node, tlv, len);
+
+	if (type == EGG_ASN1X_SET_OF)
+		tlv->sorted = 1;
+
+	return tlv;
 }
 
-static gboolean
-anode_encode_prepare (GNode *node, gboolean want)
+static Atlv *
+anode_build_maybe_explicit (GNode *node,
+                            Atlv *tlv,
+                            gint flags)
 {
+	guchar cls_type;
+	Atlv *wrap;
+
+	/* Now wrap in explicit tag if that's the case */
+	if (anode_calc_explicit_for_flags (node, flags, &cls_type)) {
+		wrap = atlv_new ();
+		wrap->cls = (ASN1_CLASS_STRUCTURED | cls_type);
+		wrap->tag = anode_calc_tag (node);
+		wrap->len = tlv->off + tlv->len;
+		wrap->off = atlv_unparse_cls_tag_len (NULL, 0, wrap->cls, wrap->tag, wrap->len);
+		wrap->child = tlv;
+		tlv = wrap;
+	}
+
+	return tlv;
+}
+
+static Atlv *
+anode_build_anything_for_flags (GNode *node,
+                                gboolean want,
+                                gint flags)
+{
+	Atlv *tlv;
+
 	switch (anode_def_type (node)) {
+	case EGG_ASN1X_BIT_STRING:
+		tlv = anode_build_bit_string (node);
+		break;
 	case EGG_ASN1X_INTEGER:
+		tlv = anode_build_integer (node);
+		break;
 	case EGG_ASN1X_BOOLEAN:
-	case EGG_ASN1X_BIT_STRING:
 	case EGG_ASN1X_OCTET_STRING:
 	case EGG_ASN1X_OBJECT_ID:
 	case EGG_ASN1X_TIME:
 	case EGG_ASN1X_ENUMERATED:
 	case EGG_ASN1X_GENERALSTRING:
-	case EGG_ASN1X_ANY:
 	case EGG_ASN1X_NULL:
-		return anode_encode_prepare_simple (node, want);
+		tlv = anode_build_value (node);
 		break;
+
+	/* Any should already have explicit tagging */
+	case EGG_ASN1X_ANY:
+		return anode_build_any (node);
+
 	case EGG_ASN1X_SEQUENCE:
 	case EGG_ASN1X_SEQUENCE_OF:
 	case EGG_ASN1X_SET:
 	case EGG_ASN1X_SET_OF:
-		return anode_encode_prepare_structured (node, want);
+		tlv = anode_build_structured (node, want);
 		break;
+
 	case EGG_ASN1X_CHOICE:
-		return anode_encode_prepare_choice (node, want);
+		tlv = anode_build_choice (node, want);
 		break;
+
 	default:
-		g_return_val_if_reached (FALSE);
-	};
-}
+		g_assert_not_reached ();
+	}
 
-typedef struct {
-	EggAllocator allocator;
-	gpointer allocated;
-} AllocatorClosure;
+	if (tlv == NULL)
+		return NULL;
 
-static void
-destroy_with_allocator (gpointer data)
-{
-	AllocatorClosure *closure = data;
-	g_assert (closure->allocator);
-	(closure->allocator) (closure->allocated, 0);
-	g_slice_free (AllocatorClosure, closure);
+	/* Now wrap in explicit tag if that's the case */
+	return anode_build_maybe_explicit (node, tlv, flags);
 }
 
-static GBytes *
-new_bytes_with_allocator (EggAllocator allocator,
-                          guchar **data,
-                          gsize length)
+static Atlv *
+anode_build_anything (GNode *node,
+                      gboolean want)
 {
-	AllocatorClosure *closure;
-
-	if (allocator) {
-		*data = (allocator) (NULL, length + 1);
-		if (allocator == NULL)
-			return NULL;
-		closure = g_slice_new (AllocatorClosure);
-		closure->allocated = *data;
-		closure->allocator = allocator;
-		return g_bytes_new_with_free_func (*data, length,
-		                                     destroy_with_allocator,
-		                                     closure);
-	} else {
-		*data = g_malloc (length);
-		return g_bytes_new_take (*data, length);
-	}
+	return anode_build_anything_for_flags (node, want,
+	                                       anode_def_flags (node));
 }
 
 GBytes *
@@ -1873,44 +1799,29 @@ egg_asn1x_encode (GNode *asn,
                   EggAllocator allocator)
 {
 	GBytes *bytes;
-	guchar *data;
-	gsize length;
 	Atlv *tlv;
 
 	g_return_val_if_fail (asn != NULL, NULL);
 	g_return_val_if_fail (anode_def_type_is_real (asn), NULL);
 
-	if (!anode_encode_prepare (asn, TRUE)) {
-		anode_failure (asn, "missing value(s)");
+	if (!egg_asn1x_validate (asn, TRUE))
 		return NULL;
-	}
-
-	/* We must sort all the nasty SET OF nodes */
-	g_node_traverse (asn, G_POST_ORDER, G_TRAVERSE_ALL, -1,
-	                 traverse_and_sort_set_of, allocator);
 
-	tlv = anode_get_tlv_data (asn);
-	g_return_val_if_fail (tlv, NULL);
-
-	/* Allocate enough memory for entire thingy */
-	length = tlv->off + tlv->len;
-	bytes = new_bytes_with_allocator (allocator, &data, length);
-	if (data == NULL)
+	tlv = anode_build_anything (asn, TRUE);
+	if (tlv == NULL) {
+		anode_failure (asn, "missing value(s)");
 		return NULL;
-
-	if (anode_encode_build (asn, bytes, data, length) &&
-	    anode_validate_anything (asn, TRUE)) {
-		anode_encode_commit (asn);
-		return bytes;
 	}
 
-	g_bytes_unref (bytes);
-	anode_encode_rollback (asn);
-	return NULL;
+	atlv_sort_perform (tlv, allocator);
+
+	bytes = atlv_unparse_to_bytes (tlv, allocator);
+	atlv_free (tlv);
+	return bytes;
 }
 
-/* -----------------------------------------------------------------------------------
- * READING, WRITING, GETTING, SETTING
+/* ----------------------------------------------------------------------------
+ * VALUE READ/WRITE
  */
 
 static int
@@ -2167,23 +2078,28 @@ parse_general_time (const gchar *time, gsize n_time,
 }
 
 static gboolean
-anode_read_time (GNode *node, Atlv *tlv, struct tm *when, glong *value)
+anode_read_time (GNode *node,
+                 GBytes *data,
+                 struct tm *when,
+                 glong *value)
 {
-	const gchar *data;
+	const gchar *buf;
 	gboolean ret;
 	gint offset = 0;
 	gint flags;
+	gsize len;
 
-	g_assert (when);
-	g_assert (value);
+	g_assert (data != NULL);
+	g_assert (when != NULL);
+	g_assert (value != NULL);
 
 	flags = anode_def_flags (node);
-	data = (gchar*)(tlv->buf + tlv->off);
+	buf = g_bytes_get_data (data, &len);
 
 	if (flags & FLAG_GENERALIZED)
-		ret = parse_general_time (data, tlv->len, when, &offset);
+		ret = parse_general_time (buf, len, when, &offset);
 	else if (flags & FLAG_UTC)
-		ret = parse_utc_time (data, tlv->len, when, &offset);
+		ret = parse_utc_time (buf, len, when, &offset);
 	else
 		g_return_val_if_reached (FALSE);
 
@@ -2205,24 +2121,29 @@ anode_read_time (GNode *node, Atlv *tlv, struct tm *when, glong *value)
 }
 
 static gboolean
-anode_read_integer_as_ulong (GNode *node, Atlv *tlv, gulong *value)
+anode_read_integer_ulong (GNode *node,
+                          GBytes *data,
+                          gulong *value)
 {
 	const guchar *p;
+	gsize len;
 	gsize k;
 
-	if (tlv->len < 1 || tlv->len > sizeof (gulong))
+	p = g_bytes_get_data (data, &len);
+	if (len < 1 || len > sizeof (gulong))
 		return FALSE;
 
-	p = tlv->buf + tlv->off;
 	*value = 0;
-	for (k = 0; k < tlv->len; ++k)
-		*value |= p[k] << (8 * ((tlv->len - 1) - k));
+	for (k = 0; k < len; ++k)
+		*value |= p[k] << (8 * ((len - 1) - k));
 
 	return TRUE;
 }
 
-static gboolean
-anode_write_integer_ulong (gulong value, guchar *data, gsize *n_data)
+static void
+anode_write_integer_ulong (gulong value,
+                           guchar *data,
+                           gsize *n_data)
 {
 	guchar buf[sizeof (gulong)];
 	gint bytes, i, off;
@@ -2234,99 +2155,158 @@ anode_write_integer_ulong (gulong value, guchar *data, gsize *n_data)
 		buf[i] = (value >> (off * 8)) & 0xFF;
 	}
 
-	for (bytes = sizeof (gulong) - 1; bytes >= 0; --bytes)
-		if (!buf[bytes])
-			break;
+	for (bytes = sizeof (gulong) - 1; bytes >= 0; --bytes)
+		if (!buf[bytes])
+			break;
+
+	bytes = sizeof (gulong) - (bytes + 1);
+	if (bytes == 0)
+		bytes = 1;
+
+	/* If the first byte would make this negative, then add a zero */
+	at = buf + (sizeof (gulong) - bytes);
+	sign = !!(at[0] & 0x80);
+
+	if (data) {
+		g_assert (*n_data >= bytes + 1);
+		if (sign) {
+			data[0] = 0;
+			data++;
+		}
+		memcpy (data, at, bytes);
+	}
+
+	*n_data = bytes + (sign ? 1 : 0);
+}
+
+static GBytes *
+anode_default_integer (GNode *node)
+{
+	const gchar *defval;
+	EggAsn1xDef *opt;
+	gchar *end;
+	gulong value;
+	guchar *data;
+	gsize len;
+
+	if (!(anode_def_flags (node) & FLAG_DEFAULT))
+		return NULL;
+
+	/* Try to get a default */
+	opt = anode_opt_lookup (node, EGG_ASN1X_DEFAULT, NULL);
+	g_return_val_if_fail (opt != NULL, NULL);
+	g_return_val_if_fail (opt->value != NULL, NULL);
+	defval = opt->value;
+
+	opt = anode_opt_lookup (node, EGG_ASN1X_CONSTANT, defval);
+	if (opt != NULL) {
+		g_return_val_if_fail (opt->value != NULL, NULL);
+		defval = opt->value;
+	}
+
+	/* Parse out the default value */
+	value = strtoul (defval, &end, 10);
+	g_return_val_if_fail (end && !end[0], NULL);
 
-	bytes = sizeof (gulong) - (bytes + 1);
-	if (bytes == 0)
-		bytes = 1;
+	anode_write_integer_ulong (value, NULL, &len);
+	data = g_malloc (len);
+	anode_write_integer_ulong (value, data, &len);
+	return g_bytes_new_take (data, len);
+}
 
-	/* If the first byte would make this negative, then add a zero */
-	at = buf + (sizeof (gulong) - bytes);
-	sign = !!(at[0] & 0x80);
+static gboolean
+anode_read_string_struct (GNode *node,
+                          Atlv *tlv,
+                          gpointer value,
+                          gsize *n_value)
+{
+	const guchar *buf;
+	gsize len;
+	Atlv *ctlv;
+	guchar *at;
+	gint remaining;
 
-	if (data) {
-		g_assert (*n_data >= bytes + 1);
-		if (sign) {
-			data[0] = 0;
-			data++;
+	g_assert (tlv != NULL);
+	g_assert (tlv->cls & ASN1_CLASS_STRUCTURED);
+	g_assert (n_value != NULL);
+
+	at = value;
+	remaining = *n_value;
+	*n_value = 0;
+
+	for (ctlv = tlv->child; ctlv != NULL; ctlv = ctlv->next) {
+		if (ctlv->cls & ASN1_CLASS_STRUCTURED ||
+		    ctlv->value == NULL)
+			return FALSE;
+		buf = g_bytes_get_data (ctlv->value, &len);
+		*n_value += len;
+		if (value) {
+			if (remaining >= len)
+				memcpy (at, buf, len);
+			at += len;
+			remaining -= len;
 		}
-		memcpy (data, at, bytes);
 	}
 
-	*n_data = bytes + (sign ? 1 : 0);
+	if (value && remaining < 0)
+		return FALSE;
+
 	return TRUE;
 }
 
 static gboolean
-anode_read_string (GNode *node, Atlv *tlv, gpointer value, gsize *n_value)
+anode_read_string_simple (GNode *node,
+                          GBytes *data,
+                          gpointer value,
+                          gsize *n_value)
 {
-	Atlv ctlv;
-	guchar *buf;
-	gint n_buf;
-	gint i;
-
-	g_assert (tlv);
-	g_assert (n_value);
+	const guchar *buf;
+	gsize len;
 
-	buf = value;
-	n_buf = *n_value;
+	g_assert (data != NULL);
+	g_assert (n_value != NULL);
 
-	/* Is it constructed ? */
-	if (tlv->cls & ASN1_CLASS_STRUCTURED) {
-		*n_value = 0;
-		for (i = 0; TRUE; ++i) {
-			if (!anode_decode_tlv_for_contents (tlv, i == 0, &ctlv))
-				return anode_failure (node, "invalid encoding of child");
-			if (ctlv.off == 0)
-				break;
-			if (ctlv.cls & ASN1_CLASS_STRUCTURED)
-				return FALSE;
-			*n_value += ctlv.len;
-			if (buf) {
-				if (n_buf >= ctlv.len)
-					memcpy (buf, ctlv.buf + ctlv.off, ctlv.len);
-				buf += ctlv.len;
-				n_buf -= ctlv.len;
-			}
-		}
-		if (n_buf < 0)
+	buf = g_bytes_get_data (data, &len);
+	if (value) {
+		if (*n_value < len) {
+			*n_value = len;
 			return FALSE;
-
-	/* Primitive, just return the contents */
-	} else {
-		*n_value = tlv->len;
-		if (buf) {
-			if (n_buf < tlv->len)
-				return FALSE;
-			memcpy (buf, tlv->buf + tlv->off, tlv->len);
 		}
+		memcpy (value, buf, len);
 	}
 
+	*n_value = len;
 	return TRUE;
 }
 
 static gboolean
-anode_read_boolean (GNode *node, Atlv *tlv, gboolean *value)
+anode_read_boolean (GNode *node,
+                    GBytes *data,
+                    gboolean *value)
 {
-	g_assert (node);
-	g_assert (tlv);
-	g_assert (value);
+	const guchar *buf;
+	gsize len;
+
+	g_assert (node != NULL);
+	g_assert (data != NULL);
+	g_assert (value != NULL);
 
-	if (tlv->len != 1)
+	buf = g_bytes_get_data (data, &len);
+	if (len != 1)
 		return FALSE;
-	if (tlv->buf[tlv->off] == 0x00)
+	if (buf[0] == 0x00)
 		*value = FALSE;
-	else if (tlv->buf[tlv->off] == 0xFF)
+	else if (buf[0] == 0xFF)
 		*value = TRUE;
 	else
 		return FALSE;
 	return TRUE;
 }
 
-static gboolean
-anode_write_boolean (gboolean value, guchar *data, gsize *n_data)
+static void
+anode_write_boolean (gboolean value,
+                     guchar *data,
+                     gsize *n_data)
 {
 	if (data) {
 		g_assert (*n_data >= 1);
@@ -2336,22 +2316,51 @@ anode_write_boolean (gboolean value, guchar *data, gsize *n_data)
 			data[0] = 0x00;
 	}
 	*n_data = 1;
-	return TRUE;
+}
+
+static GBytes *
+anode_default_boolean (GNode *node)
+{
+	EggAsn1xDef *opt;
+	gboolean value;
+	guchar *data;
+	gsize len;
+
+	if (!(anode_def_flags (node) & FLAG_DEFAULT))
+		return NULL;
+
+	/* Try to get a default */
+	opt = anode_opt_lookup (node, EGG_ASN1X_DEFAULT, NULL);
+	g_return_val_if_fail (opt != NULL, NULL);
+
+	/* Parse out the default value */
+	if ((opt->type & FLAG_TRUE) == FLAG_TRUE)
+		value = TRUE;
+	else if ((opt->type & FLAG_FALSE) == FLAG_FALSE)
+		value = FALSE;
+	else
+		g_return_val_if_reached (FALSE);
+
+	anode_write_boolean (value, NULL, &len);
+	data = g_malloc (len);
+	anode_write_boolean (value, data, &len);
+	return g_bytes_new_take (data, len);
 }
 
 static gboolean
-anode_read_object_id (GNode *node, Atlv *tlv, gchar **oid)
+anode_read_object_id (GNode *node,
+                      GBytes *data,
+                      gchar **oid)
 {
 	GString *result = NULL;
 	const guchar *p;
 	gboolean lead;
 	guint val, pval;
+	gsize len;
 	gint k;
 
-	g_assert (tlv);
-	if (tlv->len <= 0)
-		return FALSE;
-	p = tlv->buf + tlv->off;
+	g_assert (data != NULL);
+	p = g_bytes_get_data (data, &len);
 
 	if (oid)
 		result = g_string_sized_new (32);
@@ -2363,7 +2372,7 @@ anode_read_object_id (GNode *node, Atlv *tlv, gchar **oid)
 		g_string_append_printf (result, "%u.%u", pval, val);
 
 	/* TODO: Validate first byte? */
-	for (k = 1, lead = 1, val = 0, pval = 0; k < tlv->len; ++k) {
+	for (k = 1, lead = 1, val = 0, pval = 0; k < len; ++k) {
 		/* X.690: the leading byte must never be 0x80 */
 		if (lead && p[k] == 0x80) {
 			anode_failure (node, "object id encoding is invalid");
@@ -2385,7 +2394,7 @@ anode_read_object_id (GNode *node, Atlv *tlv, gchar **oid)
 		}
 	}
 
-	if (k < tlv->len) {
+	if (k < len) {
 		if (result)
 			g_string_free (result, TRUE);
 		return FALSE;
@@ -2397,7 +2406,9 @@ anode_read_object_id (GNode *node, Atlv *tlv, gchar **oid)
 }
 
 static gboolean
-anode_write_oid (const gchar *oid, guchar *data, gsize *n_data)
+anode_write_object_id (const gchar *oid,
+                       guchar *data,
+                       gsize *n_data)
 {
 	const gchar *p, *next;
 	gint num, num1;
@@ -2452,6 +2463,10 @@ anode_write_oid (const gchar *oid, guchar *data, gsize *n_data)
 	return TRUE;
 }
 
+/* -----------------------------------------------------------------------------------
+ * GETTING, SETTING
+ */
+
 GNode*
 egg_asn1x_node (GNode *asn, ...)
 {
@@ -2507,14 +2522,14 @@ egg_asn1x_node (GNode *asn, ...)
 const gchar*
 egg_asn1x_name (GNode *node)
 {
-	g_return_val_if_fail (node, NULL);
+	g_return_val_if_fail (node != NULL, NULL);
 	return anode_def_name (node);
 }
 
 EggAsn1xType
 egg_asn1x_type (GNode *node)
 {
-	g_return_val_if_fail (node, 0);
+	g_return_val_if_fail (node != NULL, 0);
 	return anode_def_type (node);
 }
 
@@ -2569,77 +2584,84 @@ egg_asn1x_append (GNode *node)
 gboolean
 egg_asn1x_have (GNode *node)
 {
-	Atlv *tlv;
+	GNode *child;
 
 	g_return_val_if_fail (node, FALSE);
 
-	/* TODO: Handle default values */
+	if (anode_get_value (node) || anode_get_parsed (node))
+		return TRUE;
+
+	for (child = node->children; child != NULL; child = child->next) {
+		if (egg_asn1x_have (child))
+			return TRUE;
+	}
 
-	tlv = anode_get_tlv_data (node);
-	return tlv != NULL && tlv->buf != NULL;
+	return FALSE;
 }
 
 gboolean
-egg_asn1x_get_boolean (GNode *node, gboolean *value)
+egg_asn1x_get_boolean (GNode *node,
+                       gboolean *value)
 {
-	EggAsn1xDef *opt;
-	Atlv *tlv;
+	gboolean ret;
+	GBytes *data;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (node != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_BOOLEAN, FALSE);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL) {
-
-		if ((anode_def_flags (node) & FLAG_DEFAULT) == 0)
-			return FALSE;
-
-		/* Try to get a default */
-		opt = anode_opt_lookup (node, EGG_ASN1X_DEFAULT, NULL);
-		g_return_val_if_fail (opt, FALSE);
-
-		/* Parse out the default value */
-		if ((opt->type & FLAG_TRUE) == FLAG_TRUE)
-			*value = TRUE;
-		else if ((opt->type & FLAG_FALSE) == FLAG_FALSE)
-			*value = FALSE;
-		else
-			g_return_val_if_reached (FALSE);
-		return TRUE;
-	}
+	data = anode_get_value (node);
+	if (data == NULL)
+		data = anode_default_boolean (node);
+	else
+		g_bytes_ref (data);
+	if (data == NULL)
+		return FALSE;
 
-	return anode_read_boolean (node, tlv, value);
+	ret = anode_read_boolean (node, data, value);
+	g_bytes_unref (data);
+	return ret;
 }
 
-gboolean
+void
 egg_asn1x_set_boolean (GNode *node, gboolean value)
 {
-	guchar *data;
-	gsize n_data;
-
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_BOOLEAN, FALSE);
+	GBytes *data, *def;
+	guchar *buf;
+	gsize len;
 
-	/* TODO: Handle default values */
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_BOOLEAN);
+
+	len = 1;
+	buf = g_malloc0 (1);
+	anode_write_boolean (value, buf, &len);
+	data = g_bytes_new_take (buf, len);
+
+	/* If it's equal to default, then clear */
+	def = anode_default_boolean (node);
+	if (def) {
+		if (g_bytes_equal (def, data)) {
+			anode_clr_value (node);
+			g_bytes_unref (data);
+			data = NULL;
+		}
+		g_bytes_unref (def);
+	}
 
-	n_data = 1;
-	data = g_malloc0 (1);
-	if (!anode_write_boolean (value, data, &n_data))
-		return FALSE;
-	anode_encode_tlv_and_enc (node, n_data, anode_encoder_data, data, g_free);
-	return TRUE;
+	if (data != NULL)
+		anode_take_value (node, data);
 }
 
-gboolean
+void
 egg_asn1x_set_null (GNode *node)
 {
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_NULL, FALSE);
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_NULL);
 
 	/* Encode zero characters */
-	anode_encode_tlv_and_enc (node, 0, anode_encoder_data, "", NULL);
-	return TRUE;
+	anode_clr_value (node);
+	anode_set_value (node, g_bytes_new_static ("", 0));
 }
 
 GQuark
@@ -2648,21 +2670,18 @@ egg_asn1x_get_enumerated (GNode *node)
 	gchar buf[sizeof (gulong) * 3];
 	EggAsn1xDef *opt;
 	gulong val;
-	Atlv *tlv;
+	GBytes *data;
 
-	g_return_val_if_fail (node, 0);
+	g_return_val_if_fail (node != NULL, 0);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_ENUMERATED, 0);
 
-	tlv = anode_get_tlv_data (node);
-
-	/* TODO: Defaults */
-
-	if (tlv == NULL || tlv->buf == NULL)
+	data = anode_get_value (node);
+	if (data == NULL)
 		return 0;
 
 	/* TODO: Signed values */
 
-	if (!anode_read_integer_as_ulong (node, tlv, &val))
+	if (!anode_read_integer_ulong (node, data, &val))
 		return 0;
 
 	/* Format that as a string */
@@ -2677,8 +2696,9 @@ egg_asn1x_get_enumerated (GNode *node)
 	return g_quark_from_static_string (opt->name);
 }
 
-gboolean
-egg_asn1x_set_enumerated (GNode *node, GQuark value)
+void
+egg_asn1x_set_enumerated (GNode *node,
+                          GQuark value)
 {
 	EggAsn1xDef *opt;
 	const gchar *name;
@@ -2686,156 +2706,143 @@ egg_asn1x_set_enumerated (GNode *node, GQuark value)
 	gsize n_data;
 	gulong val;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (value, FALSE);
-	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_ENUMERATED, FALSE);
-
-	/* TODO: Handle default values */
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (value != 0);
+	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_ENUMERATED);
 
 	name = g_quark_to_string (value);
-	g_return_val_if_fail (name, FALSE);
+	g_return_if_fail (name != NULL);
 
 	opt = anode_opt_lookup (node, EGG_ASN1X_CONSTANT, name);
-	g_return_val_if_fail (opt && opt->value, FALSE);
+	g_return_if_fail (opt && opt->value);
 
 	/* TODO: Signed values */
 
 	val = anode_def_value_as_ulong (opt);
-	g_return_val_if_fail (val != G_MAXULONG, FALSE);
+	g_return_if_fail (val != G_MAXULONG);
 
 	n_data = sizeof (gulong) + 1;
 	data = g_malloc0 (n_data);
-	if (!anode_write_integer_ulong (val, data, &n_data))
-		return FALSE;
+	anode_write_integer_ulong (val, data, &n_data);
 
-	anode_encode_tlv_and_enc (node, n_data, anode_encoder_data, data, g_free);
-	return TRUE;
+	anode_clr_value (node);
+	anode_set_value (node, g_bytes_new_take (data, n_data));
 }
 
 gboolean
-egg_asn1x_get_integer_as_ulong (GNode *node, gulong *value)
+egg_asn1x_get_integer_as_ulong (GNode *node,
+                                gulong *value)
 {
-	const EggAsn1xDef *opt;
-	const gchar *defval;
-	Atlv *tlv;
-	gchar *end;
+	gboolean ret;
+	GBytes *data;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (node != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, FALSE);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL) {
-
-		if ((anode_def_flags (node) & FLAG_DEFAULT) == 0)
-			return FALSE;
-
-		/* Try to get a default */
-		opt = anode_opt_lookup (node, EGG_ASN1X_DEFAULT, NULL);
-		g_return_val_if_fail (opt, FALSE);
-		g_return_val_if_fail (opt->value, FALSE);
-		defval = opt->value;
-
-		opt = anode_opt_lookup (node, EGG_ASN1X_CONSTANT, defval);
-		if (opt != NULL) {
-			g_return_val_if_fail (opt->value, FALSE);
-			defval = opt->value;
-		}
-
-		/* Parse out the default value */
-		*value = strtoul (defval, &end, 10);
-		g_return_val_if_fail (end && !end[0], FALSE);
-		return TRUE;
-	}
+	data = anode_get_value (node);
+	if (data == NULL)
+		data = anode_default_integer (node);
+	else
+		g_bytes_ref (data);
+	if (data == NULL)
+		return FALSE;
 
-	return anode_read_integer_as_ulong (node, tlv, value);
+	ret = anode_read_integer_ulong (node, data, value);
+	g_bytes_unref (data);
+	return ret;
 }
 
-gboolean
-egg_asn1x_set_integer_as_ulong (GNode *node, gulong value)
+void
+egg_asn1x_set_integer_as_ulong (GNode *node,
+                                gulong value)
 {
-	guchar *data;
-	gsize n_data;
+	GBytes *data, *def;
+	guchar *buf;
+	gsize len;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, FALSE);
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER);
 
-	/* TODO: Handle default values */
+	len = sizeof (gulong) + 1;
+	buf = g_malloc0 (len);
+	anode_write_integer_ulong (value, buf, &len);
+	data = g_bytes_new_take (buf, len);
+
+	/* If it's equal to default, then clear */
+	def = anode_default_integer (node);
+	if (def) {
+		if (g_bytes_equal (def, data)) {
+			anode_clr_value (node);
+			g_bytes_unref (data);
+			data = NULL;
+		}
+		g_bytes_unref (def);
+	}
 
-	n_data = sizeof (gulong) + 1;
-	data = g_malloc0 (n_data);
-	if (!anode_write_integer_ulong (value, data, &n_data))
-		return FALSE;
-	anode_encode_tlv_and_enc (node, n_data, anode_encoder_data, data, g_free);
-	return TRUE;
+	if (data != NULL)
+		anode_take_value (node, data);
 }
 
 GBytes *
 egg_asn1x_get_integer_as_raw (GNode *node)
 {
-	GBytes *backing;
-	Atlv *tlv;
-
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, FALSE);
+	Anode *an;
+	GBytes *raw;
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
-		return NULL;
+	g_return_val_if_fail (node != NULL, NULL);
+	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, NULL);
 
-	backing = anode_get_backing (node);
-	if (backing == NULL)
+	an = node->data;
+	if (an->guarantee_unsigned) {
+		g_warning ("cannot read integer set with egg_asn1x_set_integer_as_raw() "
+		           "via egg_asn1x_get_integer_as_raw()");
 		return NULL;
+	}
 
-	return g_bytes_new_with_free_func (tlv->buf + tlv->off, tlv->len,
-	                                   (GDestroyNotify)g_bytes_unref,
-	                                   g_bytes_ref (backing));
+	raw = anode_get_value (node);
+	if (raw != NULL)
+		g_bytes_ref (raw);
+	return raw;
 }
 
 GBytes *
 egg_asn1x_get_integer_as_usg (GNode *node)
 {
-	GBytes *backing;
 	const guchar *p;
+	Anode *an;
 	gboolean sign;
-	Atlv *tlv;
-	gsize n_data;
 	gsize len;
 
 	g_return_val_if_fail (node != NULL, FALSE);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER, FALSE);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
-		return NULL;
-
-	backing = anode_get_backing (node);
-	if (backing == NULL)
-		return NULL;
-
-	p = tlv->buf + tlv->off;
-	len = tlv->len;
-
-	sign = !!(p[0] & 0x80);
-	if (sign) {
-		g_warning ("invalid two's complement integer is negative, but expected unsigned");
+	an = node->data;
+	if (an->value == NULL)
 		return NULL;
-	}
 
-	n_data = len;
+	p = g_bytes_get_data (an->value, &len);
 
-	/* Strip off the extra zero byte that was preventing it from being negative */
-	if (p[0] == 0 && len > 1) {
-		sign = !!(p[1] & 0x80);
+	if (!an->guarantee_unsigned) {
+		sign = !!(p[0] & 0x80);
 		if (sign) {
-			p++;
-			n_data = len - 1;
+			g_warning ("invalid two's complement integer is negative, but expected unsigned");
+			return NULL;
+		}
+
+		/* Strip off the extra zero byte that was preventing it from being negative */
+		if (p[0] == 0 && len > 1) {
+			sign = !!(p[1] & 0x80);
+			if (sign) {
+				p++;
+				len--;
+			}
 		}
 	}
 
-	return g_bytes_new_with_free_func (p, n_data,
+	return g_bytes_new_with_free_func (p, len,
 	                                   (GDestroyNotify)g_bytes_unref,
-	                                   g_bytes_ref (backing));
+	                                   g_bytes_ref (an->value));
 }
 
 void
@@ -2852,183 +2859,244 @@ egg_asn1x_take_integer_as_raw (GNode *node,
 {
 	gboolean sign;
 	const guchar *p;
+	Anode *an;
+
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (value != NULL);
+	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER);
+
+	/* Make sure the integer is properly encoded in twos complement*/
+	p = g_bytes_get_data (value, NULL);
+	g_return_if_fail (p != NULL);
+
+	sign = !!(p[0] & 0x80);
+	if (sign) {
+		g_warning ("integer in egg_asn1x_set_integer_as_raw is not two's complement");
+		return;
+	}
+
+	anode_clr_value (node);
+	anode_set_value (node, value);
+
+	an = node->data;
+	an->guarantee_unsigned = 0;
+}
+
+void
+egg_asn1x_set_integer_as_usg (GNode *node,
+                              GBytes *value)
+{
+	g_return_if_fail (value != NULL);
+	egg_asn1x_take_integer_as_usg (node, g_bytes_ref (value));
+}
+
+void
+egg_asn1x_take_integer_as_usg (GNode *node,
+                               GBytes *value)
+{
+	Anode *an;
+
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (value != NULL);
+	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER);
+
+	anode_set_value (node, value);
+	an = node->data;
+	an->guarantee_unsigned = 1;
+}
+
+GNode *
+egg_asn1x_get_any_as (GNode *node,
+                      const EggAsn1xDef *defs,
+                      const gchar *type)
+{
+	g_return_val_if_fail (node != NULL, NULL);
+	g_return_val_if_fail (type != NULL, NULL);
+	g_return_val_if_fail (egg_asn1x_type (node) == EGG_ASN1X_ANY, NULL);
+
+	return egg_asn1x_get_any_as_full (node, defs, type, 0);
+}
+
+GNode *
+egg_asn1x_get_any_as_full (GNode *node,
+                           const EggAsn1xDef *defs,
+                           const gchar *type,
+                           gint options)
+{
+	GNode *asn;
+
+	g_return_val_if_fail (node != NULL, NULL);
+	g_return_val_if_fail (type != NULL, NULL);
+	g_return_val_if_fail (egg_asn1x_type (node) == EGG_ASN1X_ANY, NULL);
+
+	asn = egg_asn1x_create (defs, type);
+	g_return_val_if_fail (asn != NULL, NULL);
+
+	if (!egg_asn1x_get_any_into_full (node, asn, options)) {
+		egg_asn1x_destroy (asn);
+		return NULL;
+	}
+
+	return asn;
+}
+
+gboolean
+egg_asn1x_get_any_into  (GNode *node,
+                         GNode *into)
+{
+	g_return_val_if_fail (node != NULL, FALSE);
+	g_return_val_if_fail (into != NULL, FALSE);
+	g_return_val_if_fail (egg_asn1x_type (node) == EGG_ASN1X_ANY, FALSE);
+
+	return egg_asn1x_get_any_into_full (node, into, 0);
+}
+
+gboolean
+egg_asn1x_get_any_into_full (GNode *node,
+                             GNode *into,
+                             gint options)
+{
+	Atlv *tlv;
 
-	g_return_if_fail (node != NULL);
-	g_return_if_fail (value != NULL);
-	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER);
+	g_return_val_if_fail (node != NULL, FALSE);
+	g_return_val_if_fail (into != NULL, FALSE);
+	g_return_val_if_fail (egg_asn1x_type (node) == EGG_ASN1X_ANY, FALSE);
 
-	/* Make sure the integer is properly encoded in twos complement*/
-	p = g_bytes_get_data (value, NULL);
-	g_return_if_fail (p != NULL);
+	tlv = anode_get_parsed (node);
+	if (tlv == NULL)
+		return FALSE;
 
-	sign = !!(p[0] & 0x80);
-	if (sign) {
-		g_warning ("integer in egg_asn1x_set_integer_as_raw is not two's complement");
-		return;
+	/* If this node is explicit, then just get the contents */
+	if (anode_calc_explicit_for_flags (node, anode_def_flags (node), NULL)) {
+		tlv = tlv->child;
+		if (tlv == NULL)
+			return FALSE;
 	}
 
-	anode_encode_tlv_and_enc (node, g_bytes_get_size (value), anode_encoder_bytes,
-	                          value, (GDestroyNotify)g_bytes_unref);
-}
+	if (!anode_decode_anything (into, tlv))
+		return FALSE;
 
-void
-egg_asn1x_set_integer_as_usg (GNode *node,
-                              GBytes *value)
-{
-	g_return_if_fail (value != NULL);
-	egg_asn1x_take_integer_as_usg (node, g_bytes_ref (value));
+	return egg_asn1x_validate (into, !(options & EGG_ASN1X_NO_STRICT));
 }
 
 void
-egg_asn1x_take_integer_as_usg (GNode *node,
-                               GBytes *value)
+egg_asn1x_set_any_from (GNode *node,
+                        GNode *from)
 {
-	gboolean sign;
-	const guchar *p;
-	gsize len;
+	Anode *an;
+	Atlv *tlv;
 
 	g_return_if_fail (node != NULL);
-	g_return_if_fail (value != NULL);
-	g_return_if_fail (anode_def_type (node) == EGG_ASN1X_INTEGER);
+	g_return_if_fail (from != NULL);
+	g_return_if_fail (egg_asn1x_type (node) == EGG_ASN1X_ANY);
 
-	/* Make sure the integer is properly encoded in twos complement*/
-	p = g_bytes_get_data (value, &len);
-	g_return_if_fail (p != NULL);
-
-	sign = !!(p[0] & 0x80);
+	tlv = anode_build_anything (from, TRUE);
+	g_return_if_fail (tlv != NULL);
 
-	/*
-	 * If in two's complement this would be negative, add a zero byte so
-	 * that it isn't. Here we just note that the result will be one byte
-	 * longer. In anode_encoder_unsigned we actually add the zero byte.
-	 */
-	if (sign)
-		len += 1;
+	/* Wrap this if necessary */
+	tlv = anode_build_maybe_explicit (node, tlv, anode_def_flags (node));
 
-	anode_encode_tlv_and_enc (node, len, anode_encoder_unsigned,
-	                          value, (GDestroyNotify)g_bytes_unref);
+	/* Mark down the tlvs for this node */
+	an = node->data;
+	atlv_free (an->parsed);
+	an->parsed = tlv;
 }
 
 GBytes *
-egg_asn1x_get_element_raw (GNode *node)
+egg_asn1x_get_any_raw (GNode *node,
+                       EggAllocator allocator)
 {
-	GBytes *backing;
-	const guchar *p;
-	gsize len;
+	GBytes *bytes;
 	Atlv *tlv;
 
 	g_return_val_if_fail (node != NULL, NULL);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
+	tlv = anode_build_anything (node, TRUE);
+	if (tlv == NULL) {
+		anode_failure (node, "missing value(s)");
 		return NULL;
-
-	backing = anode_get_backing (node);
-	if (backing == NULL)
-		return NULL;
-
-	if (anode_calc_explicit (node, NULL)) {
-		len = (tlv->len + tlv->off) - tlv->oft;
-		p = tlv->buf + tlv->oft;
-	} else {
-		len = tlv->len + tlv->off;
-		p = tlv->buf;
 	}
 
-	return g_bytes_new_with_free_func (p, len, (GDestroyNotify)g_bytes_unref,
-	                                   g_bytes_ref (backing));
+	atlv_sort_perform (tlv, allocator);
+
+	bytes = atlv_unparse_to_bytes (tlv, allocator);
+	atlv_free (tlv);
+	return bytes;
 }
 
 gboolean
-egg_asn1x_set_element_raw (GNode *node,
-                           GBytes *element)
+egg_asn1x_set_any_raw (GNode *node,
+                       GBytes *raw)
 {
-	Atlv dtlv, *tlv;
-	gint oft, flags;
-	const guchar *data;
-	guchar cls_type;
-	GBytes *sub;
-	gsize size;
+	const gchar *msg;
+	Anode *an;
+	Atlv *tlv;
 
 	g_return_val_if_fail (node != NULL, FALSE);
-	g_return_val_if_fail (element != NULL, FALSE);
-
-	anode_clear (node);
-	memset (&dtlv, 0, sizeof (dtlv));
+	g_return_val_if_fail (raw != NULL, FALSE);
 
-	data = g_bytes_get_data (element, &size);
-	g_return_val_if_fail (data != NULL, FALSE);
+	an = node->data;
+	tlv = atlv_new ();
+	msg = atlv_parse_der (raw, tlv);
+	if (msg == NULL) {
 
-	/* Decode the beginning TLV */
-	if (!anode_decode_tlv_for_data (data, data + size, &dtlv))
-		return FALSE;
+		/* Wrap this if necessary */
+		tlv = anode_build_maybe_explicit (node, tlv, anode_def_flags (node));
 
-	/*
-	 * Decode the data into place properly, to make sure it fits. Note
-	 * we are not decoding any explicit outer tagging, this is just
-	 * the internal value. In addition we do not support optional
-	 * and default values, which would decode successfully in
-	 * unexpected ways.
-	 */
-	flags = anode_def_flags (node);
-	flags &= ~(FLAG_TAG | FLAG_DEFAULT | FLAG_OPTION);
-	if (!anode_decode_anything_for_flags (node, element, &dtlv, flags))
-		return FALSE;
+		atlv_free (an->parsed);
+		an->parsed = tlv;
+		return TRUE;
 
-	/* There was extra data */
-	if (dtlv.end - dtlv.buf != size)
+	/* A failure, set the message manually so it doesn't get a prefix */
+	} else {
+		an = node->data;
+		g_free (an->failure);
+		an->failure = g_strdup (msg);
 		return FALSE;
-
-	/* Clear buffer from TLV so it gets encoded */
-	tlv = anode_get_tlv_data (node);
-	g_assert (tlv);
-	tlv->buf = tlv->end = NULL;
-
-	/* Explicit tagging: leave space for the outer tag */
-	if (anode_calc_explicit (node, &cls_type)) {
-		oft = anode_encode_cls_tag_len (NULL, 0, (ASN1_CLASS_STRUCTURED | cls_type),
-		                                anode_calc_tag (node), size);
-
-		tlv->off += oft;
-		tlv->oft = oft;
 	}
-
-	sub = g_bytes_new_with_free_func (dtlv.buf + dtlv.off, dtlv.len,
-	                                  (GDestroyNotify)g_bytes_unref,
-	                                  g_bytes_ref (element));
-
-	/* Setup encoding of the contents */
-	anode_set_enc_data (node, anode_encoder_bytes, sub, (GDestroyNotify)g_bytes_unref);
-	return TRUE;
 }
 
 GBytes *
-egg_asn1x_get_raw_value (GNode *node)
+egg_asn1x_get_element_raw (GNode *node)
 {
-	GBytes *backing;
+	Anode *an;
 	Atlv *tlv;
 
-	g_return_val_if_fail (node, NULL);
+	g_return_val_if_fail (node != NULL, NULL);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
-		return NULL;
-	g_return_val_if_fail (!(tlv->cls & ASN1_CLASS_STRUCTURED), NULL);
+	an = node->data;
+	tlv = an->parsed;
+
+	/* If this node is explicit, then just get the contents */
+	if (tlv && anode_calc_explicit_for_flags (node, anode_def_flags (node), NULL))
+		tlv = tlv->child;
 
-	backing = anode_get_backing (node);
-	if (backing == NULL)
+	if (!tlv || !tlv->decoded)
 		return NULL;
 
-	return g_bytes_new_with_free_func (tlv->buf + tlv->off, tlv->len,
-	                                   (GDestroyNotify)g_bytes_unref,
-	                                   g_bytes_ref (backing));
+	return g_bytes_ref (tlv->decoded);
+}
+
+GBytes *
+egg_asn1x_get_value_raw (GNode *node)
+{
+	GBytes *raw;
+
+	g_return_val_if_fail (node != NULL, NULL);
+	raw = anode_get_value (node);
+	if (raw != NULL)
+		g_bytes_ref (raw);
+	return raw;
 }
 
-guchar*
-egg_asn1x_get_string_as_raw (GNode *node, EggAllocator allocator, gsize *n_string)
+guchar *
+egg_asn1x_get_string_as_raw (GNode *node,
+                             EggAllocator allocator,
+                             gsize *n_string)
 {
 	gsize length;
 	guchar *string;
+	GBytes *data;
 	Atlv *tlv;
 	gint type;
 
@@ -3039,43 +3107,68 @@ egg_asn1x_get_string_as_raw (GNode *node, EggAllocator allocator, gsize *n_strin
 		allocator = g_realloc;
 
 	type = anode_def_type (node);
-	g_return_val_if_fail (type == EGG_ASN1X_OCTET_STRING || type == EGG_ASN1X_GENERALSTRING, NULL);
+	g_return_val_if_fail (type == EGG_ASN1X_OCTET_STRING ||
+	                      type == EGG_ASN1X_GENERALSTRING, NULL);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
-		return NULL;
+	data = anode_get_value (node);
+	if (data != NULL) {
+		if (!anode_read_string_simple (node, data, NULL, &length))
+			return NULL;
 
-	if (!anode_read_string (node, tlv, NULL, &length))
-		return NULL;
+		string = (allocator) (NULL, length + 1);
+		if (string == NULL)
+			return NULL;
 
-	string = (allocator) (NULL, length + 1);
-	if (string == NULL)
-		return NULL;
+		if (!anode_read_string_simple (node, data, string, &length)) {
+			(allocator) (string, 0);
+			return NULL;
+		}
 
-	if (!anode_read_string (node, tlv, string, &length)) {
-		(allocator) (string, 0);
-		return NULL;
+		/* Courtesy null termination, string must however be validated! */
+		string[length] = 0;
+		*n_string = length;
+		return string;
 	}
 
-	/* Courtesy null termination, string must however be validated! */
-	string[length] = 0;
-	*n_string = length;
-	return string;
+	tlv = anode_get_parsed (node);
+	if (tlv != NULL) {
+		if (!anode_read_string_struct (node, tlv, NULL, &length))
+			return NULL;
+
+		string = (allocator) (NULL, length + 1);
+		if (string == NULL)
+			return NULL;
+
+		if (!anode_read_string_struct (node, tlv, string, &length)) {
+			(allocator) (string, 0);
+			return NULL;
+		}
+
+		/* Courtesy null termination, string must however be validated! */
+		string[length] = 0;
+		*n_string = length;
+		return string;
+	}
+
+	return NULL;
 }
 
-gboolean
-egg_asn1x_set_string_as_raw (GNode *node, guchar *data, gsize n_data, GDestroyNotify destroy)
+void
+egg_asn1x_set_string_as_raw (GNode *node,
+                             guchar *data,
+                             gsize n_data,
+                             GDestroyNotify destroy)
 {
 	gint type;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (data, FALSE);
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (data != NULL);
 
 	type = anode_def_type (node);
-	g_return_val_if_fail (type == EGG_ASN1X_OCTET_STRING || type == EGG_ASN1X_GENERALSTRING, FALSE);
+	g_return_if_fail (type == EGG_ASN1X_OCTET_STRING || type == EGG_ASN1X_GENERALSTRING);
 
-	anode_encode_tlv_and_enc (node, n_data, anode_encoder_data, data, destroy);
-	return TRUE;
+	anode_set_value (node, g_bytes_new_with_free_func (data, n_data,
+	                                                   destroy, data));
 }
 
 GBytes *
@@ -3113,7 +3206,8 @@ egg_asn1x_get_bmpstring_as_utf8 (GNode *node)
 }
 
 gchar*
-egg_asn1x_get_string_as_utf8 (GNode *node, EggAllocator allocator)
+egg_asn1x_get_string_as_utf8 (GNode *node,
+                              EggAllocator allocator)
 {
 	gchar *string;
 	gsize n_string;
@@ -3136,47 +3230,44 @@ egg_asn1x_get_string_as_utf8 (GNode *node, EggAllocator allocator)
 }
 
 gboolean
-egg_asn1x_set_string_as_utf8 (GNode *node, gchar *data, GDestroyNotify destroy)
+egg_asn1x_set_string_as_utf8 (GNode *node,
+                              gchar *data,
+                              GDestroyNotify destroy)
 {
 	gsize n_data;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (data, FALSE);
+	g_return_val_if_fail (node != NULL, FALSE);
+	g_return_val_if_fail (data != NULL, FALSE);
 
 	n_data = strlen (data);
 	if (!g_utf8_validate (data, n_data, NULL))
 		return FALSE;
 
-	return egg_asn1x_set_string_as_raw (node, (guchar*)data, n_data, destroy);
+	egg_asn1x_set_string_as_raw (node, (guchar*)data, n_data, destroy);
+	return TRUE;
 }
 
 GBytes *
-egg_asn1x_get_bits_as_raw (GNode *node, guint *n_bits)
+egg_asn1x_get_bits_as_raw (GNode *node,
+                           guint *n_bits)
 {
-	GBytes *backing;
-	guchar padded;
-	Atlv *tlv;
-
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (n_bits, FALSE);
-	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_BIT_STRING, FALSE);
+	gsize len;
+	GBytes *data;
+	Anode *an;
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
-		return NULL;
+	g_return_val_if_fail (node != NULL, NULL);
+	g_return_val_if_fail (n_bits != NULL, NULL);
+	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_BIT_STRING, NULL);
 
-	backing = anode_get_backing (node);
-	if (backing == NULL)
+	data = anode_get_value (node);
+	if (data == NULL)
 		return NULL;
 
-	padded = *(tlv->buf + tlv->off);
-	g_return_val_if_fail (padded < 8, NULL);
-	g_return_val_if_fail (tlv->len > 1, NULL);
+	len = g_bytes_get_size (data);
+	an = node->data;
 
-	*n_bits = ((tlv->len - 1) * 8) - padded;
-	return g_bytes_new_with_free_func (tlv->buf + tlv->off + 1, tlv->len - 1,
-	                                   (GDestroyNotify)g_bytes_unref,
-	                                   g_bytes_ref (backing));
+	*n_bits = (len * 8) - an->bits_empty;
+	return g_bytes_ref (data);
 }
 
 void
@@ -3184,7 +3275,9 @@ egg_asn1x_set_bits_as_raw (GNode *node,
                            GBytes *value,
                            guint n_bits)
 {
+	g_return_if_fail (node != NULL);
 	g_return_if_fail (value != NULL);
+
 	egg_asn1x_take_bits_as_raw (node, g_bytes_ref (value), n_bits);
 }
 
@@ -3193,9 +3286,10 @@ egg_asn1x_take_bits_as_raw (GNode *node,
                             GBytes *value,
                             guint n_bits)
 {
+	Anode *an;
 	gint type;
-	gsize length;
-	Abits *ab;
+	gsize len;
+	guchar empty;
 
 	g_return_if_fail (node != NULL);
 	g_return_if_fail (value != NULL);
@@ -3203,47 +3297,54 @@ egg_asn1x_take_bits_as_raw (GNode *node,
 	type = anode_def_type (node);
 	g_return_if_fail (type == EGG_ASN1X_BIT_STRING);
 
-	length = (n_bits / 8);
+	len = (n_bits / 8);
 	if (n_bits % 8)
-		length += 1;
+		len += 1;
 
-	ab = g_slice_new0 (Abits);
-	ab->bits = value;
-	ab->n_bits = n_bits;
+	empty = n_bits % 8;
+	if (empty > 0)
+		empty = 8 - empty;
 
-	anode_encode_tlv_and_enc (node, length + 1, anode_encoder_bit_string, ab, abits_destroy);
+	anode_take_value (node, value);
+	an = node->data;
+	an->bits_empty = empty;
 }
 
 gboolean
-egg_asn1x_get_bits_as_ulong (GNode *node, gulong *bits, guint *n_bits)
+egg_asn1x_get_bits_as_ulong (GNode *node,
+                             gulong *bits,
+                             guint *n_bits)
 {
-	Atlv *tlv;
+	GBytes *data;
+	const guchar *buf;
+	gsize len;
 	guint i, length;
 	guchar empty;
 	const guchar *p;
 	gulong value;
+	Anode *an;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (bits, FALSE);
-	g_return_val_if_fail (n_bits, FALSE);
+	g_return_val_if_fail (node != NULL, FALSE);
+	g_return_val_if_fail (bits != NULL, FALSE);
+	g_return_val_if_fail (n_bits != NULL, FALSE);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_BIT_STRING, FALSE);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
+	data = anode_get_value (node);
+	if (data == NULL)
 		return FALSE;
 
-	empty = *(tlv->buf + tlv->off);
-	g_return_val_if_fail (empty < 8, FALSE);
-	g_return_val_if_fail (tlv->len > 1, FALSE);
+	buf = g_bytes_get_data (data, &len);
+	an = node->data;
+	empty = an->bits_empty;
 
-	length = ((tlv->len - 1) * 8) - empty;
+	length = (len * 8) - empty;
 	if (length > sizeof (gulong) * 8)
 		return FALSE;
 
 	value = 0;
-	p = tlv->buf + tlv->off + 1;
+	p = buf;
 
-	for (i = 0; i < tlv->len - 1; ++i)
+	for (i = 0; i < len; ++i)
 		value = value << 8 | p[i];
 
 	*bits = value >> empty;
@@ -3251,47 +3352,45 @@ egg_asn1x_get_bits_as_ulong (GNode *node, gulong *bits, guint *n_bits)
 	return TRUE;
 }
 
-gboolean
-egg_asn1x_set_bits_as_ulong (GNode *node, gulong bits, guint n_bits)
+void
+egg_asn1x_set_bits_as_ulong (GNode *node,
+                             gulong bits,
+                             guint n_bits)
 {
 	guchar *data;
 	gulong value;
-	gint type;
-	gsize i, length;
+	gsize i, len;
 	guchar empty;
-	Abits *ab;
+	Anode *an;
+	gint type;
 
-	g_return_val_if_fail (node, FALSE);
-	g_return_val_if_fail (bits, FALSE);
-	g_return_val_if_fail (n_bits <= sizeof (gulong) * 8, FALSE);
+	g_return_if_fail (node != NULL);
+	g_return_if_fail (n_bits <= sizeof (gulong) * 8);
 
 	type = anode_def_type (node);
-	g_return_val_if_fail (type == EGG_ASN1X_BIT_STRING, FALSE);
+	g_return_if_fail (type == EGG_ASN1X_BIT_STRING);
 
 	empty = n_bits % 8;
 	if (empty > 0)
 		empty = 8 - empty;
-	length = (n_bits / 8) + (empty ? 1 : 0);
+	len = (n_bits / 8) + (empty ? 1 : 0);
 
 	data = g_malloc0 (sizeof (gulong));
 	value = bits << empty;
 
-	for (i = 0; i < length; ++i)
-		data[(length - i) - 1] = (value >> i * 8) & 0xFF;
+	for (i = 0; i < len; ++i)
+		data[len - i - 1] = (value >> i * 8) & 0xFF;
 
-	ab = g_slice_new0 (Abits);
-	ab->bits = g_bytes_new_take (data, sizeof (gulong));
-	ab->n_bits = n_bits;
-
-	anode_encode_tlv_and_enc (node, length + 1, anode_encoder_bit_string, ab, abits_destroy);
-	return TRUE;
+	an = node->data;
+	an->bits_empty = empty;
+	anode_take_value (node, g_bytes_new_take (data, len));
 }
 
 glong
 egg_asn1x_get_time_as_long (GNode *node)
 {
 	struct tm when;
-	Atlv *tlv;
+	GBytes *data;
 	glong time;
 	gint type;
 
@@ -3309,20 +3408,21 @@ egg_asn1x_get_time_as_long (GNode *node)
 
 	g_return_val_if_fail (type == EGG_ASN1X_TIME, -1);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
+	data = anode_get_value (node);
+	if (data == NULL)
 		return -1;
 
-	if (!anode_read_time (node, tlv, &when, &time))
+	if (!anode_read_time (node, data, &when, &time))
 		return -1;
 	return time;
 }
 
 gboolean
-egg_asn1x_get_time_as_date (GNode *node, GDate *date)
+egg_asn1x_get_time_as_date (GNode *node,
+                            GDate *date)
 {
 	struct tm when;
-	Atlv *tlv;
+	GBytes *data;
 	glong time;
 	gint type;
 
@@ -3340,11 +3440,11 @@ egg_asn1x_get_time_as_date (GNode *node, GDate *date)
 
 	g_return_val_if_fail (type == EGG_ASN1X_TIME, FALSE);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
+	data = anode_get_value (node);
+	if (data == NULL)
 		return FALSE;
 
-	if (!anode_read_time (node, tlv, &when, &time))
+	if (!anode_read_time (node, data, &when, &time))
 		return FALSE;
 
 	g_date_set_dmy (date, when.tm_mday, when.tm_mon + 1, when.tm_year + 1900);
@@ -3354,42 +3454,43 @@ egg_asn1x_get_time_as_date (GNode *node, GDate *date)
 gchar*
 egg_asn1x_get_oid_as_string (GNode *node)
 {
+	GBytes *data;
 	gchar *oid;
-	Atlv *tlv;
 
 	g_return_val_if_fail (node, NULL);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_OBJECT_ID, NULL);
 
-	tlv = anode_get_tlv_data (node);
-	if (tlv == NULL || tlv->buf == NULL)
+	data = anode_get_value (node);
+	if (data == NULL)
 		return NULL;
 
-	if (!anode_read_object_id (node, tlv, &oid))
+	if (!anode_read_object_id (node, data, &oid))
 		return NULL;
 
 	return oid;
 }
 
 gboolean
-egg_asn1x_set_oid_as_string (GNode *node, const gchar *oid)
+egg_asn1x_set_oid_as_string (GNode *node,
+                             const gchar *oid)
 {
 	guchar *data;
 	gsize n_data;
 
-	g_return_val_if_fail (oid, FALSE);
-	g_return_val_if_fail (node, FALSE);
+	g_return_val_if_fail (oid != NULL, FALSE);
+	g_return_val_if_fail (node != NULL, FALSE);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_OBJECT_ID, FALSE);
 
 	/* Encoding will always be shorter than string */
 	n_data = strlen (oid);
 	data = g_malloc0 (n_data);
 
-	if (!anode_write_oid (oid, data, &n_data)) {
+	if (!anode_write_object_id (oid, data, &n_data)) {
 		g_free (data);
 		return FALSE;
 	}
 
-	anode_encode_tlv_and_enc (node, n_data, anode_encoder_data, data, g_free);
+	anode_take_value (node, g_bytes_new_take (data, n_data));
 	return TRUE;
 }
 
@@ -3408,13 +3509,15 @@ egg_asn1x_get_oid_as_quark (GNode *node)
 }
 
 gboolean
-egg_asn1x_set_oid_as_quark (GNode *node, GQuark oid)
+egg_asn1x_set_oid_as_quark (GNode *node,
+                            GQuark oid)
 {
 	const gchar *str;
 
-	g_return_val_if_fail (oid, FALSE);
+	g_return_val_if_fail (oid != 0, FALSE);
+
 	str = g_quark_to_string (oid);
-	g_return_val_if_fail (str, FALSE);
+	g_return_val_if_fail (str != NULL, FALSE);
 
 	return egg_asn1x_set_oid_as_string (node, str);
 }
@@ -3438,12 +3541,13 @@ egg_asn1x_get_choice (GNode *node)
 }
 
 gboolean
-egg_asn1x_set_choice (GNode *node, GNode *choice)
+egg_asn1x_set_choice (GNode *node,
+                      GNode *choice)
 {
 	GNode *child;
 	Anode *an;
 
-	g_return_val_if_fail (node, FALSE);
+	g_return_val_if_fail (node != NULL, FALSE);
 	g_return_val_if_fail (anode_def_type (node) == EGG_ASN1X_CHOICE, FALSE);
 
 	/* One and only one of the children must be set */
@@ -3468,7 +3572,9 @@ egg_asn1x_set_choice (GNode *node, GNode *choice)
  */
 
 static gboolean
-anode_parse_size (GNode *node, const gchar *text, gulong *value)
+anode_parse_size (GNode *node,
+                  const gchar *text,
+                  gulong *value)
 {
 	EggAsn1xDef *def;
 	gchar *end = NULL;
@@ -3492,7 +3598,8 @@ anode_parse_size (GNode *node, const gchar *text, gulong *value)
 
 
 static gboolean
-anode_validate_size (GNode *node, gulong length)
+anode_validate_size (GNode *node,
+                     gulong length)
 {
 	EggAsn1xDef *size;
 	gulong value1 = 0;
@@ -3518,23 +3625,26 @@ anode_validate_size (GNode *node, gulong length)
 }
 
 static gboolean
-anode_validate_integer (GNode *node, Atlv *tlv)
+anode_validate_integer (GNode *node,
+                        GBytes *value)
 {
 	GList *constants, *l;
-	gulong value, check;
+	gulong val, check;
+	gsize len;
 	gboolean found;
 	gint flags;
 
-	g_assert (tlv);
+	g_assert (value != NULL);
+	len = g_bytes_get_size (value);
 
 	/* Integers must be at least one byte long */
-	if (tlv->len <= 0)
+	if (len == 0)
 		return anode_failure (node, "zero length integer");
 
 	flags = anode_def_flags (node);
 	if (flags & FLAG_LIST) {
 		/* Parse out the value, we only support small integers*/
-		if (!anode_read_integer_as_ulong (node, tlv, &value))
+		if (!anode_read_integer_ulong (node, value, &val))
 			return anode_failure (node, "integer not part of list");
 
 		/* Look through the list of constants */
@@ -3543,7 +3653,7 @@ anode_validate_integer (GNode *node, Atlv *tlv)
 		for (l = constants; l; l = g_list_next (l)) {
 			check = anode_def_value_as_ulong (l->data);
 			g_return_val_if_fail (check != G_MAXULONG, FALSE);
-			if (check == value) {
+			if (check == val) {
 				found = TRUE;
 				break;
 			}
@@ -3558,82 +3668,88 @@ anode_validate_integer (GNode *node, Atlv *tlv)
 }
 
 static gboolean
-anode_validate_enumerated (GNode *node, Atlv *tlv)
+anode_validate_enumerated (GNode *node,
+                           GBytes *value)
 {
-	g_assert (tlv);
+	const guchar *buf;
+	gsize length;
+
+	g_assert (value != NULL);
 
-	if (!anode_validate_integer (node, tlv))
+	if (!anode_validate_integer (node, value))
 		return FALSE;
-	g_assert (tlv->len); /* Checked above */
+
+	buf = g_bytes_get_data (value, &length);
+	g_assert (length > 0); /* Checked above */
+
 	/* Enumerated must be positive */
-	if (tlv->buf[tlv->off] & 0x80)
+	if (buf[0] & 0x80)
 		return anode_failure (node, "enumerated must be positive");
 	return TRUE;
 }
 
 static gboolean
-anode_validate_boolean (GNode *node, Atlv *tlv)
+anode_validate_boolean (GNode *node,
+                        GBytes *value)
 {
-	g_assert (tlv);
+	const guchar *buf;
+	gsize len;
+
+	g_assert (value != NULL);
+	buf = g_bytes_get_data (value, &len);
 
 	/* Must one byte, and zero or all ones */
-	if (tlv->len != 1)
+	if (len != 1)
 		return anode_failure (node, "invalid length boolean");
-	if (tlv->buf[tlv->off] != 0x00 && tlv->buf[tlv->off] != 0xFF)
+	if (buf[0] != 0x00 && buf[0] != 0xFF)
 		return anode_failure (node, "boolean must be true or false");
 	return TRUE;
 }
 
 static gboolean
-anode_validate_bit_string (GNode *node, Atlv *tlv)
+anode_validate_bit_string (GNode *node,
+                           GBytes *value)
 {
-	guchar empty, mask;
-	g_assert (tlv);
+	g_assert (value != NULL);
 
-	/* At least two bytes in length */
-	if (tlv->len < 1)
-		return anode_failure (node, "invalid length bit string");
-	/* First byte is the number of free bits at end */
-	empty = tlv->buf[tlv->off];
-	if (empty > 7)
-		return anode_failure (node, "bit string has invalid header");
-	/* Free bits at end must be zero */
-	mask = 0xFF >> (8 - empty);
-	if (tlv->len > 1 && tlv->buf[tlv->off + tlv->len - 1] & mask)
-		return anode_failure (node, "bit string has invalid trailing bits");
+	/* All the decode validation done in anode_decode_bit_string */
 	return TRUE;
 }
 
 static gboolean
-anode_validate_string (GNode *node, Atlv *tlv)
+anode_validate_string (GNode *node,
+                       GBytes *value)
 {
 	gsize length;
 
-	if (!anode_read_string (node, tlv, NULL, &length))
+	if (!anode_read_string_simple (node, value, NULL, &length))
 		return anode_failure (node, "string content is invalid");
 
 	return anode_validate_size (node, (gulong)length);
 }
 
 static gboolean
-anode_validate_object_id (GNode *node, Atlv *tlv)
+anode_validate_object_id (GNode *node,
+                          GBytes *value)
 {
-	return anode_read_object_id (node, tlv, NULL);
+	return anode_read_object_id (node, value, NULL);
 }
 
 static gboolean
-anode_validate_null (GNode *node, Atlv *tlv)
+anode_validate_null (GNode *node,
+                     GBytes *value)
 {
-	g_assert (tlv);
-	return (tlv->len == 0);
+	g_assert (value != NULL);
+	return (g_bytes_get_size (value) == 0);
 }
 
 static gboolean
-anode_validate_time (GNode *node, Atlv *tlv)
+anode_validate_time (GNode *node,
+                     GBytes *value)
 {
 	glong time;
 	struct tm when;
-	return anode_read_time (node, tlv, &when, &time);
+	return anode_read_time (node, value, &when, &time);
 }
 
 static gboolean
@@ -3667,25 +3783,12 @@ anode_validate_sequence_or_set (GNode *node,
                                 gboolean strict)
 {
 	GNode *child;
-	gulong tag = 0;
-	gint count = 0;
-	gint type;
-	Atlv *tlv;
-
-	type = anode_def_type (node);
 
 	/* All of the children must validate properly */
 	for (child = node->children; child; child = child->next) {
-		if (!anode_validate_anything (child, strict))
-			return FALSE;
-
-		/* Tags must be in ascending order */
-		tlv = anode_get_tlv_data (child);
-		if (tlv && type == EGG_ASN1X_SET) {
-			if (count > 0 && tag > tlv->tag)
-				return anode_failure (node, "content must be in ascending order");
-			tag = tlv->tag;
-			++count;
+		if (egg_asn1x_have (child)) {
+			if (!anode_validate_anything (child, strict))
+				return FALSE;
 		}
 	}
 
@@ -3697,36 +3800,16 @@ anode_validate_sequence_or_set_of (GNode *node,
                                    gboolean strict)
 {
 	GNode *child;
-	Atlv *tlv, *ptlv;
-	gulong tag;
 	gulong count;
-	gint type;
 
-	tag = 0;
 	count = 0;
-	ptlv = NULL;
-
-	type = anode_def_type (node);
 
 	/* All the children must validate properly */
 	for (child = node->children; child; child = child->next) {
-		tlv = anode_get_tlv_data (child);
-		if (tlv) {
+		if (egg_asn1x_have (child)) {
 			if (!anode_validate_anything (child, strict))
 				return FALSE;
-
-			/* Tag must have same tag as top */
-			if (count == 0)
-				tag = anode_calc_tag (child);
-			else if (tag != G_MAXULONG && tlv->tag != tag)
-				return anode_failure (node, "invalid mismatched content");
-
-			/* Set of must be in ascending order */
-			if (strict && type == EGG_ASN1X_SET_OF &&
-			    ptlv != NULL && compare_tlvs (ptlv, tlv) > 0)
-				return anode_failure (node, "content must be in ascending order");
-			ptlv = tlv;
-			++count;
+			count++;
 		}
 	}
 
@@ -3737,51 +3820,17 @@ static gboolean
 anode_validate_anything (GNode *node,
                          gboolean strict)
 {
+	GBytes *value;
 	Atlv *tlv;
 	gint type;
 
 	type = anode_def_type (node);
-	tlv = anode_get_tlv_data (node);
-
-	if (!tlv) {
-		if (anode_def_flags (node) & FLAG_OPTION)
-			return TRUE;
-		if (anode_def_flags (node) & FLAG_DEFAULT)
-			return TRUE;
-		return anode_failure (node, "missing value");
-	}
-
-	g_return_val_if_fail (tlv->buf, FALSE);
 
+	/* Handle these specially */
 	switch (type) {
-
-	/* The primitive value types */
-	case EGG_ASN1X_INTEGER:
-		return anode_validate_integer (node, tlv);
-	case EGG_ASN1X_ENUMERATED:
-		return anode_validate_enumerated (node, tlv);
-	case EGG_ASN1X_BOOLEAN:
-		return anode_validate_boolean (node, tlv);
-	case EGG_ASN1X_BIT_STRING:
-		return anode_validate_bit_string (node, tlv);
-	case EGG_ASN1X_OCTET_STRING:
-		return anode_validate_string (node, tlv);
-	case EGG_ASN1X_OBJECT_ID:
-		return anode_validate_object_id (node, tlv);
-	case EGG_ASN1X_NULL:
-		return anode_validate_null (node, tlv);
-	case EGG_ASN1X_GENERALSTRING:
-		return anode_validate_string (node, tlv);
-	case EGG_ASN1X_TIME:
-		return anode_validate_time (node, tlv);
-
-	/* Transparent types */
-	case EGG_ASN1X_ANY:
-		return TRUE;
 	case EGG_ASN1X_CHOICE:
 		return anode_validate_choice (node, strict);
 
-	/* Structured types */
 	case EGG_ASN1X_SEQUENCE:
 	case EGG_ASN1X_SET:
 		return anode_validate_sequence_or_set (node, strict);
@@ -3791,8 +3840,54 @@ anode_validate_anything (GNode *node,
 		return anode_validate_sequence_or_set_of (node, strict);
 
 	default:
-		g_return_val_if_reached (FALSE);
+		break;
+	}
+
+	/* Values that have been configured */
+	value = anode_get_value (node);
+	if (value) {
+		switch (type) {
+		case EGG_ASN1X_INTEGER:
+			return anode_validate_integer (node, value);
+		case EGG_ASN1X_ENUMERATED:
+			return anode_validate_enumerated (node, value);
+		case EGG_ASN1X_BOOLEAN:
+			return anode_validate_boolean (node, value);
+		case EGG_ASN1X_BIT_STRING:
+			return anode_validate_bit_string (node, value);
+		case EGG_ASN1X_OCTET_STRING:
+			return anode_validate_string (node, value);
+		case EGG_ASN1X_OBJECT_ID:
+			return anode_validate_object_id (node, value);
+		case EGG_ASN1X_NULL:
+			return anode_validate_null (node, value);
+		case EGG_ASN1X_GENERALSTRING:
+			return anode_validate_string (node, value);
+		case EGG_ASN1X_TIME:
+			return anode_validate_time (node, value);
+		default:
+			g_assert_not_reached ();
+		}
 	}
+
+	/* See if there's a tlv parsed */
+	tlv = anode_get_parsed (node);
+	if (tlv) {
+		switch (type) {
+		case EGG_ASN1X_ANY:
+		case EGG_ASN1X_GENERALSTRING:
+		case EGG_ASN1X_OCTET_STRING:
+			return TRUE;
+		default:
+			break;
+		}
+	}
+
+	if (anode_def_flags (node) & FLAG_OPTION)
+		return TRUE;
+	if (anode_def_flags (node) & FLAG_DEFAULT)
+		return TRUE;
+	return anode_failure (node, "missing value");
 }
 
 gboolean
@@ -4169,25 +4264,40 @@ egg_asn1x_create_quark (const EggAsn1xDef *defs,
 	return egg_asn1x_create (defs, g_quark_to_string (type));
 }
 
-GNode*
-egg_asn1x_create_and_decode (const EggAsn1xDef *defs,
-                             const gchar *identifier,
-                             GBytes *data)
+GNode *
+egg_asn1x_create_and_decode_full (const EggAsn1xDef *defs,
+                                  const gchar *identifier,
+                                  GBytes *data,
+                                  gint options)
 {
 	GNode *asn;
 
-	g_return_val_if_fail (defs, NULL);
-	g_return_val_if_fail (identifier, NULL);
+	g_return_val_if_fail (defs != NULL, NULL);
+	g_return_val_if_fail (identifier != NULL, NULL);
+	g_return_val_if_fail (data != NULL, NULL);
 
 	asn = egg_asn1x_create (defs, identifier);
 	g_return_val_if_fail (asn, NULL);
 
-	if (!egg_asn1x_decode (asn, data)) {
+	if (!egg_asn1x_decode_full (asn, data, options)) {
 		egg_asn1x_destroy (asn);
 		return NULL;
 	}
 
 	return asn;
+
+}
+
+GNode*
+egg_asn1x_create_and_decode (const EggAsn1xDef *defs,
+                             const gchar *identifier,
+                             GBytes *data)
+{
+	g_return_val_if_fail (defs != NULL, NULL);
+	g_return_val_if_fail (identifier != NULL, NULL);
+	g_return_val_if_fail (data != NULL, NULL);
+
+	return egg_asn1x_create_and_decode_full (defs, identifier, data, 0);
 }
 
 /* -----------------------------------------------------------------------------------
@@ -4225,7 +4335,6 @@ traverse_and_dump (GNode *node, gpointer unused)
 	guint i, depth;
 	GString *output;
 	gchar *string;
-	Atlv *tlv;
 	Anode *an;
 	GList *l;
 
@@ -4233,18 +4342,17 @@ traverse_and_dump (GNode *node, gpointer unused)
 	for (i = 0; i < depth - 1; ++i)
 		g_printerr ("    ");
 
-	tlv = anode_get_tlv_data (node);
+	an = node->data;
 	output = g_string_new ("");
 	dump_append_type (output, anode_def_type (node));
 	dump_append_flags (output, anode_def_flags (node));
 	string = g_utf8_casefold (output->str, output->len - 1);
 	g_string_free (output, TRUE);
 	g_printerr ("+ %s: %s [%s]%s\n", anode_def_name (node), anode_def_value (node),
-	            string, tlv && tlv->buf ? " *" : "");
+	            string, an->parsed || an->value ? " *" : "");
 	g_free (string);
 
 	/* Print out all the options */
-	an = node->data;
 	for (l = an->opts; l; l = g_list_next (l)) {
 		for (i = 0; i < depth; ++i)
 			g_printerr ("    ");
@@ -4416,9 +4524,9 @@ egg_asn1x_element_length (const guchar *data,
 	int cb, len;
 	gulong tag;
 
-	if (anode_decode_cls_tag (data, data + n_data, &cls, &tag, &cb)) {
+	if (atlv_parse_cls_tag (data, data + n_data, &cls, &tag, &cb)) {
 		counter += cb;
-		len = anode_decode_length (data + cb, data + n_data, &cb);
+		len = atlv_parse_length (data + cb, data + n_data, &cb);
 		counter += cb;
 		if (len >= 0) {
 			len += counter;
@@ -4444,11 +4552,11 @@ egg_asn1x_element_content (const guchar *data,
 	g_return_val_if_fail (n_content != NULL, NULL);
 
 	/* Now get the data out of this element */
-	if (!anode_decode_cls_tag (data, (const guchar*)data + n_data, &cls, &tag, &cb))
+	if (!atlv_parse_cls_tag (data, data + n_data, &cls, &tag, &cb))
 		return NULL;
 
 	counter += cb;
-	len = anode_decode_length ((const guchar*)data + cb, (const guchar*)data + n_data, &cb);
+	len = atlv_parse_length (data + cb, data + n_data, &cb);
 	if (len < 0)
 		return NULL;
 	counter += cb;
diff --git a/egg/egg-asn1x.h b/egg/egg-asn1x.h
index 036b9ce..bca01e0 100644
--- a/egg/egg-asn1x.h
+++ b/egg/egg-asn1x.h
@@ -58,6 +58,10 @@ typedef enum {
 	EGG_ASN1X_GENERALSTRING = 27
 } EggAsn1xType;
 
+enum {
+	EGG_ASN1X_NO_STRICT = 0x01,
+} EggAsn1xFlags;
+
 GNode*              egg_asn1x_create                 (const EggAsn1xDef *defs,
                                                       const gchar *type);
 
@@ -68,6 +72,11 @@ GNode*              egg_asn1x_create_and_decode      (const EggAsn1xDef *defs,
                                                       const gchar *type,
                                                       GBytes *data);
 
+GNode*              egg_asn1x_create_and_decode_full (const EggAsn1xDef *defs,
+                                                      const gchar *type,
+                                                      GBytes *data,
+                                                      gint options);
+
 void                egg_asn1x_dump                   (GNode *asn);
 
 void                egg_asn1x_clear                  (GNode *asn);
@@ -75,13 +84,39 @@ void                egg_asn1x_clear                  (GNode *asn);
 gboolean            egg_asn1x_decode                 (GNode *asn,
                                                       GBytes *data);
 
-gboolean            egg_asn1x_decode_no_validate     (GNode *asn,
-                                                      GBytes *data);
+gboolean            egg_asn1x_decode_full            (GNode *asn,
+                                                      GBytes *data,
+                                                      gint options);
+
+void                egg_asn1x_set_any_from           (GNode *node,
+                                                      GNode *from);
+
+gboolean            egg_asn1x_set_any_raw            (GNode *node,
+                                                      GBytes *raw);
+
+gboolean            egg_asn1x_get_any_into           (GNode *node,
+                                                      GNode *into);
+
+gboolean            egg_asn1x_get_any_into_full      (GNode *node,
+                                                      GNode *into,
+                                                      gint options);
+
+GNode *             egg_asn1x_get_any_as             (GNode *node,
+                                                      const EggAsn1xDef *defs,
+                                                      const gchar *type);
+
+GNode *             egg_asn1x_get_any_as_full        (GNode *node,
+                                                      const EggAsn1xDef *defs,
+                                                      const gchar *type,
+                                                      gint options);
+
+GBytes *            egg_asn1x_get_any_raw            (GNode *node,
+                                                      EggAllocator allocator);
 
 gboolean            egg_asn1x_validate               (GNode *asn,
                                                       gboolean strict);
 
-GBytes *          egg_asn1x_encode                 (GNode *asn,
+GBytes *            egg_asn1x_encode                 (GNode *asn,
                                                       EggAllocator allocator);
 
 const gchar*        egg_asn1x_message                (GNode *asn);
@@ -107,23 +142,23 @@ gboolean            egg_asn1x_set_choice             (GNode *node,
 gboolean            egg_asn1x_get_boolean            (GNode *node,
                                                       gboolean *value);
 
-gboolean            egg_asn1x_set_boolean            (GNode *node,
+void                egg_asn1x_set_boolean            (GNode *node,
                                                       gboolean value);
 
-gboolean            egg_asn1x_set_null               (GNode *node);
+void                egg_asn1x_set_null               (GNode *node);
 
 GQuark              egg_asn1x_get_enumerated         (GNode *node);
 
-gboolean            egg_asn1x_set_enumerated         (GNode *node,
+void                egg_asn1x_set_enumerated         (GNode *node,
                                                       GQuark value);
 
 gboolean            egg_asn1x_get_integer_as_ulong   (GNode *node,
                                                       gulong *value);
 
-gboolean            egg_asn1x_set_integer_as_ulong   (GNode *node,
+void                egg_asn1x_set_integer_as_ulong   (GNode *node,
                                                       gulong value);
 
-GBytes *          egg_asn1x_get_integer_as_raw     (GNode *node);
+GBytes *            egg_asn1x_get_integer_as_raw     (GNode *node);
 
 void                egg_asn1x_set_integer_as_raw     (GNode *node,
                                                       GBytes *value);
@@ -131,7 +166,7 @@ void                egg_asn1x_set_integer_as_raw     (GNode *node,
 void                egg_asn1x_take_integer_as_raw    (GNode *node,
                                                       GBytes *value);
 
-GBytes *          egg_asn1x_get_integer_as_usg     (GNode *node);
+GBytes *            egg_asn1x_get_integer_as_usg     (GNode *node);
 
 void                egg_asn1x_set_integer_as_usg     (GNode *node,
                                                       GBytes *value);
@@ -139,25 +174,22 @@ void                egg_asn1x_set_integer_as_usg     (GNode *node,
 void                egg_asn1x_take_integer_as_usg    (GNode *node,
                                                       GBytes *value);
 
-GBytes *          egg_asn1x_get_raw_value          (GNode *node);
+GBytes *            egg_asn1x_get_value_raw          (GNode *node);
 
-GBytes *          egg_asn1x_get_element_raw        (GNode *node);
-
-gboolean            egg_asn1x_set_element_raw        (GNode *node,
-                                                      GBytes *value);
+GBytes *            egg_asn1x_get_element_raw        (GNode *node);
 
 guchar*             egg_asn1x_get_string_as_raw      (GNode *node,
                                                       EggAllocator allocator,
                                                       gsize *n_string);
 
-gboolean            egg_asn1x_set_string_as_raw      (GNode *node,
+void                egg_asn1x_set_string_as_raw      (GNode *node,
                                                       guchar *data,
                                                       gsize n_data,
                                                       GDestroyNotify destroy);
 
-GBytes *          egg_asn1x_get_string_as_bytes    (GNode *node);
+GBytes *            egg_asn1x_get_string_as_bytes    (GNode *node);
 
-GBytes *          egg_asn1x_get_bits_as_raw        (GNode *node,
+GBytes *            egg_asn1x_get_bits_as_raw        (GNode *node,
                                                       guint *n_bits);
 
 void                egg_asn1x_set_bits_as_raw        (GNode *node,
@@ -172,7 +204,7 @@ gboolean            egg_asn1x_get_bits_as_ulong      (GNode *node,
                                                       gulong *value,
                                                       guint *n_bits);
 
-gboolean            egg_asn1x_set_bits_as_ulong      (GNode *node,
+void                egg_asn1x_set_bits_as_ulong      (GNode *node,
                                                       gulong value,
                                                       guint n_bits);
 
diff --git a/egg/egg-dn.c b/egg/egg-dn.c
index e3b092e..43bd772 100644
--- a/egg/egg-dn.c
+++ b/egg/egg-dn.c
@@ -52,7 +52,7 @@ dn_print_hex_value (GBytes *val)
 static gchar*
 dn_print_oid_value_parsed (GQuark oid,
                            guint flags,
-                           GBytes *val)
+                           GNode *val)
 {
 	GNode *asn1, *node;
 	GBytes *value;
@@ -65,7 +65,7 @@ dn_print_oid_value_parsed (GQuark oid,
 	asn1 = egg_asn1x_create_quark (pkix_asn1_tab, oid);
 	g_return_val_if_fail (asn1, NULL);
 
-	if (!egg_asn1x_decode (asn1, val)) {
+	if (!egg_asn1x_get_any_into (val, asn1)) {
 		g_message ("couldn't decode value for OID: %s: %s",
 		           g_quark_to_string (oid), egg_asn1x_message (asn1));
 		egg_asn1x_destroy (asn1);
@@ -81,7 +81,7 @@ dn_print_oid_value_parsed (GQuark oid,
 	else
 		node = asn1;
 
-	value = egg_asn1x_get_raw_value (node);
+	value = egg_asn1x_get_value_raw (node);
 	data = g_bytes_get_data (value, &size);
 
 	/*
@@ -108,8 +108,9 @@ dn_print_oid_value_parsed (GQuark oid,
 static gchar*
 dn_print_oid_value (GQuark oid,
                     guint flags,
-                    GBytes *val)
+                    GNode *val)
 {
+	GBytes *der;
 	gchar *value;
 
 	g_assert (val != NULL);
@@ -120,7 +121,11 @@ dn_print_oid_value (GQuark oid,
 			return value;
 	}
 
-	return dn_print_hex_value (val);
+	der = egg_asn1x_get_element_raw (val);
+	value = dn_print_hex_value (der);
+	g_bytes_unref (der);
+
+	return value;
 }
 
 static gchar*
@@ -129,7 +134,7 @@ dn_parse_rdn (GNode *asn)
 	const gchar *name;
 	guint flags;
 	GQuark oid;
-	GBytes *value;
+	GNode *value;
 	gchar *display;
 	gchar *result;
 
@@ -141,7 +146,7 @@ dn_parse_rdn (GNode *asn)
 	flags = egg_oid_get_flags (oid);
 	name = egg_oid_get_name (oid);
 
-	value = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "value", NULL));
+	value = egg_asn1x_node (asn, "value", NULL);
 	g_return_val_if_fail (value, NULL);
 
 	display = dn_print_oid_value (oid, flags, value);
@@ -149,7 +154,6 @@ dn_parse_rdn (GNode *asn)
 	                      "=", display, NULL);
 	g_free (display);
 
-	g_bytes_unref (value);
 	return result;
 }
 
@@ -200,11 +204,9 @@ egg_dn_read_part (GNode *asn, const gchar *match)
 {
 	gboolean done = FALSE;
 	const gchar *name;
-	GBytes *value;
 	GNode *node;
 	GQuark oid;
 	gint i, j;
-	gchar *result;
 
 	g_return_val_if_fail (asn, NULL);
 	g_return_val_if_fail (match, NULL);
@@ -233,12 +235,7 @@ egg_dn_read_part (GNode *asn, const gchar *match)
 			node = egg_asn1x_node (asn, i, j, "value", NULL);
 			g_return_val_if_fail (node, NULL);
 
-			value = egg_asn1x_get_element_raw (node);
-			g_return_val_if_fail (value, NULL);
-
-			result = dn_print_oid_value (oid, egg_oid_get_flags (oid), value);
-			g_bytes_unref (value);
-			return result;
+			return dn_print_oid_value (oid, egg_oid_get_flags (oid), node);
 		}
 	}
 
@@ -250,7 +247,6 @@ egg_dn_parse (GNode *asn, EggDnCallback callback, gpointer user_data)
 {
 	gboolean done = FALSE;
 	GNode *node;
-	GBytes *value;
 	GQuark oid;
 	guint i, j;
 
@@ -279,12 +275,8 @@ egg_dn_parse (GNode *asn, EggDnCallback callback, gpointer user_data)
 				break;
 			}
 
-			value = egg_asn1x_get_element_raw (node);
-
 			if (callback)
-				(callback) (i, oid, value, user_data);
-
-			g_bytes_unref (value);
+				(callback) (i, oid, node, user_data);
 		}
 	}
 
@@ -293,7 +285,7 @@ egg_dn_parse (GNode *asn, EggDnCallback callback, gpointer user_data)
 
 gchar *
 egg_dn_print_value (GQuark oid,
-                    GBytes *value)
+                    GNode *value)
 {
 	g_return_val_if_fail (oid != 0, NULL);
 	g_return_val_if_fail (value != NULL, NULL);
@@ -336,7 +328,6 @@ egg_dn_add_string_part (GNode *asn,
                         GQuark oid,
                         const gchar *string)
 {
-	GBytes *bytes;
 	GNode *node;
 	GNode *value;
 	GNode *val;
@@ -373,15 +364,6 @@ egg_dn_add_string_part (GNode *asn,
 
 	egg_asn1x_set_string_as_utf8 (val, g_strdup (string), g_free);
 
-	bytes = egg_asn1x_encode (value, NULL);
-	if (bytes == NULL) {
-		g_warning ("couldn't build dn string value: %s", egg_asn1x_message (value));
-		return;
-	}
-
-	if (!egg_asn1x_set_element_raw (egg_asn1x_node (node, "value", NULL), bytes))
-		g_return_if_reached ();
-
+	egg_asn1x_set_any_from (egg_asn1x_node (node, "value", NULL), value);
 	egg_asn1x_destroy (value);
-	g_bytes_unref (bytes);
 }
diff --git a/egg/egg-dn.h b/egg/egg-dn.h
index a75e73d..bdf6b92 100644
--- a/egg/egg-dn.h
+++ b/egg/egg-dn.h
@@ -33,7 +33,7 @@ gchar*             egg_dn_read_part                       (GNode *node,
 
 typedef void       (*EggDnCallback)                       (guint index,
                                                            GQuark oid,
-                                                           GBytes *value,
+                                                           GNode *value,
                                                            gpointer user_data);
 
 gboolean           egg_dn_parse                           (GNode *node,
@@ -41,7 +41,7 @@ gboolean           egg_dn_parse                           (GNode *node,
                                                            gpointer user_data);
 
 gchar*             egg_dn_print_value                     (GQuark oid,
-                                                           GBytes *value);
+                                                           GNode *value);
 
 void               egg_dn_add_string_part                 (GNode *node,
                                                            GQuark oid,
diff --git a/egg/egg-symkey.c b/egg/egg-symkey.c
index d5459a5..54592d9 100644
--- a/egg/egg-symkey.c
+++ b/egg/egg-symkey.c
@@ -662,7 +662,7 @@ read_cipher_pkcs5_pbe (int cipher_algo,
                        int hash_algo,
                        const gchar *password,
                        gsize n_password,
-                       GBytes *data,
+                       GNode *data,
                        gcry_cipher_hd_t *cih)
 {
 	GNode *asn = NULL;
@@ -689,10 +689,10 @@ read_cipher_pkcs5_pbe (int cipher_algo,
 	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-5-PBE-params");
 	g_return_val_if_fail (asn, FALSE);
 
-	if (!egg_asn1x_decode (asn, data))
+	if (!egg_asn1x_get_any_into (data, asn))
 		goto done;
 
-	salt = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "salt", NULL));
+	salt = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "salt", NULL));
 	if (!salt)
 		goto done;
 	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "iterationCount", NULL), &iterations))
@@ -730,7 +730,7 @@ done:
 }
 
 static gboolean
-setup_pkcs5_rc2_params (GBytes *data,
+setup_pkcs5_rc2_params (GNode *any,
                         gcry_cipher_hd_t cih)
 {
 	GNode *asn = NULL;
@@ -739,18 +739,16 @@ setup_pkcs5_rc2_params (GBytes *data,
 	gulong version;
 	gboolean ret = FALSE;
 
-	g_assert (data);
+	g_assert (any != NULL);
 
-	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-5-rc2-CBC-params");
-	g_return_val_if_fail (asn, FALSE);
-
-	if (!egg_asn1x_decode (asn, data))
+	asn = egg_asn1x_get_any_as (any, pkix_asn1_tab, "pkcs-5-rc2-CBC-params");
+	if (asn == NULL)
 		goto done;
 
 	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "rc2ParameterVersion", NULL), &version))
 		goto done;
 
-	iv = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "iv", NULL));
+	iv = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "iv", NULL));
 	if (!iv)
 		goto done;
 
@@ -770,7 +768,7 @@ done:
 }
 
 static gboolean
-setup_pkcs5_des_params (GBytes *data,
+setup_pkcs5_des_params (GNode *any,
                         gcry_cipher_hd_t cih)
 {
 	GNode *asn = NULL;
@@ -778,15 +776,15 @@ setup_pkcs5_des_params (GBytes *data,
 	GBytes *iv;
 	gboolean ret;
 
-	g_assert (data);
+	g_assert (any != NULL);
 
-	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-5-des-EDE3-CBC-params", data);
+	asn = egg_asn1x_get_any_as (any, pkix_asn1_tab, "pkcs-5-des-EDE3-CBC-params");
 	if (!asn)
-		asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-5-des-CBC-params", data);
+		asn = egg_asn1x_get_any_as (any, pkix_asn1_tab, "pkcs-5-des-CBC-params");
 	if (!asn)
 		return FALSE;
 
-	iv = egg_asn1x_get_raw_value (asn);
+	iv = egg_asn1x_get_string_as_bytes (asn);
 	egg_asn1x_destroy (asn);
 
 	if (!iv)
@@ -807,7 +805,7 @@ setup_pkcs5_des_params (GBytes *data,
 static gboolean
 setup_pkcs5_pbkdf2_params (const gchar *password,
                            gsize n_password,
-                           GBytes *data,
+                           GNode *any,
                            int cipher_algo,
                            gcry_cipher_hd_t cih)
 {
@@ -820,17 +818,17 @@ setup_pkcs5_pbkdf2_params (const gchar *password,
 	gulong iterations;
 
 	g_assert (cipher_algo);
-	g_assert (data != NULL);
+	g_assert (any != NULL);
 
 	ret = FALSE;
 
-	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-5-PBKDF2-params", data);
+	asn = egg_asn1x_get_any_as (any, pkix_asn1_tab, "pkcs-5-PBKDF2-params");
 	if (!asn)
 		goto done;
 
 	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "iterationCount", NULL), &iterations))
 		iterations = 1;
-	salt = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "salt", "specified", NULL));
+	salt = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "salt", "specified", NULL));
 	if (!salt)
 		goto done;
 
@@ -861,13 +859,13 @@ done:
 static gboolean
 read_cipher_pkcs5_pbes2 (const gchar *password,
                          gsize n_password,
-                         GBytes *data,
+                         GNode *data,
                          gcry_cipher_hd_t *cih)
 {
 	GNode *asn = NULL;
 	gboolean r, ret;
 	GQuark key_deriv_algo, enc_oid;
-	GBytes *params = NULL;
+	GNode *params = NULL;
 	gcry_error_t gcry;
 	int algo, mode;
 
@@ -879,7 +877,7 @@ read_cipher_pkcs5_pbes2 (const gchar *password,
 	*cih = NULL;
 	ret = FALSE;
 
-	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-5-PBES2-params", data);
+	asn = egg_asn1x_get_any_as (data, pkix_asn1_tab, "pkcs-5-PBES2-params");
 	if (!asn)
 		goto done;
 
@@ -910,7 +908,7 @@ read_cipher_pkcs5_pbes2 (const gchar *password,
 	}
 
 	/* Read out the parameters */
-	params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "encryptionScheme", "parameters", NULL));
+	params = egg_asn1x_node (asn, "encryptionScheme", "parameters", NULL);
 	if (!params)
 		goto done;
 
@@ -941,8 +939,7 @@ read_cipher_pkcs5_pbes2 (const gchar *password,
 		goto done;
 	}
 
-	g_bytes_unref (params);
-	params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "keyDerivationFunc", "parameters", NULL));
+	params = egg_asn1x_node (asn, "keyDerivationFunc", "parameters", NULL);
 	if (!params)
 		goto done;
 
@@ -954,8 +951,6 @@ done:
 		*cih = NULL;
 	}
 
-	if (params != NULL)
-		g_bytes_unref (params);
 	egg_asn1x_destroy (asn);
 	return ret;
 }
@@ -965,7 +960,7 @@ read_cipher_pkcs12_pbe (int cipher_algo,
                         int cipher_mode,
                         const gchar *password,
                         gsize n_password,
-                        GBytes *data,
+                        GNode *data,
                         gcry_cipher_hd_t *cih)
 {
 	GNode *asn = NULL;
@@ -988,11 +983,11 @@ read_cipher_pkcs12_pbe (int cipher_algo,
 	if (gcry_cipher_algo_info (cipher_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0)
 		goto done;
 
-	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-12-PbeParams", data);
+	asn = egg_asn1x_get_any_as (data, pkix_asn1_tab, "pkcs-12-PbeParams");
 	if (!asn)
 		goto done;
 
-	salt = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "salt", NULL));
+	salt = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "salt", NULL));
 	if (!salt)
 		goto done;
 	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "iterations", NULL), &iterations))
@@ -1037,7 +1032,7 @@ static gboolean
 read_mac_pkcs12_pbe (int hash_algo,
                      const gchar *password,
                      gsize n_password,
-                     GBytes *data,
+                     GNode *data,
                      gcry_md_hd_t *mdh,
                      gsize *digest_len)
 {
@@ -1060,14 +1055,17 @@ read_mac_pkcs12_pbe (int hash_algo,
 	if (gcry_md_algo_info (hash_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0)
 		goto done;
 
-	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-12-MacData", data);
-	if (!asn)
-		goto done;
+	if (egg_asn1x_type (data) == EGG_ASN1X_ANY) {
+		asn = egg_asn1x_get_any_as (data, pkix_asn1_tab, "pkcs-12-MacData");
+		if (!asn)
+			goto done;
+		data = asn;
+	}
 
-	salt = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "macSalt", NULL));
+	salt = egg_asn1x_get_string_as_bytes (egg_asn1x_node (data, "macSalt", NULL));
 	if (!salt)
 		goto done;
-	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "iterations", NULL), &iterations))
+	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (data, "iterations", NULL), &iterations))
 		goto done;
 
 	n_key = gcry_md_get_algo_dlen (hash_algo);
@@ -1107,7 +1105,7 @@ gboolean
 egg_symkey_read_cipher (GQuark oid_scheme,
                         const gchar *password,
                         gsize n_password,
-                        GBytes *data,
+                        GNode *data,
                         gcry_cipher_hd_t *cih)
 {
 	gboolean ret = FALSE;
@@ -1175,7 +1173,7 @@ gboolean
 egg_symkey_read_mac (GQuark oid_scheme,
                      const gchar *password,
                      gsize n_password,
-                     GBytes *data,
+                     GNode *data,
                      gcry_md_hd_t *mdh,
                      gsize *digest_len)
 {
diff --git a/egg/egg-symkey.h b/egg/egg-symkey.h
index 8498613..f4938f6 100644
--- a/egg/egg-symkey.h
+++ b/egg/egg-symkey.h
@@ -77,13 +77,13 @@ gboolean                 egg_symkey_generate_pbkdf2             (int cipher_algo
 gboolean                 egg_symkey_read_cipher                 (GQuark oid_scheme,
                                                                  const gchar *password,
                                                                  gsize n_password,
-                                                                 GBytes *data,
+                                                                 GNode *params,
                                                                  gcry_cipher_hd_t *cih);
 
 gboolean                 egg_symkey_read_mac                    (GQuark oid_scheme,
                                                                  const gchar *password,
                                                                  gsize n_password,
-                                                                 GBytes *data,
+                                                                 GNode *params,
                                                                  gcry_md_hd_t *mdh,
                                                                  gsize *digest_len);
 
diff --git a/egg/tests/Makefile.am b/egg/tests/Makefile.am
index e736f85..0f47a06 100644
--- a/egg/tests/Makefile.am
+++ b/egg/tests/Makefile.am
@@ -22,6 +22,7 @@ LDADD =  \
 
 TEST_PROGS = \
 	test-asn1 \
+	test-asn1x \
 	test-dn \
 	test-decimal \
 	test-hex \
@@ -54,15 +55,3 @@ EXTRA_DIST = \
 
 CLEANFILES = \
 	$(ASN_SRCS)
-
-# ------------------------------------------------------------------------------
-
-noinst_PROGRAMS = \
-	test-asn1x
-
-test_asn1x_SOURCES = \
-	test-asn1x.c
-
-test_asn1x_LDADD = \
-	$(top_builddir)/egg/libegg-asn1x.la \
-	$(LDADD)
diff --git a/egg/tests/files/test-personalname-1.der b/egg/tests/files/test-personalname-1.der
index 60d5d8c..13ba718 100644
--- a/egg/tests/files/test-personalname-1.der
+++ b/egg/tests/files/test-personalname-1.der
@@ -1 +1 @@
-1€TurangaLeelaƒAlien
\ No newline at end of file
+1€TurangaLeelaƒII
\ No newline at end of file
diff --git a/egg/tests/files/test-personalname-invalid.der b/egg/tests/files/test-personalname-invalid.der
new file mode 100644
index 0000000..60d5d8c
--- /dev/null
+++ b/egg/tests/files/test-personalname-invalid.der
@@ -0,0 +1 @@
+1€TurangaLeelaƒAlien
\ No newline at end of file
diff --git a/egg/tests/files/test-pkcs12-2.der b/egg/tests/files/test-pkcs12-2.der
new file mode 100644
index 0000000..eff8c1e
Binary files /dev/null and b/egg/tests/files/test-pkcs12-2.der differ
diff --git a/egg/tests/test-asn1.c b/egg/tests/test-asn1.c
index 95667de..1313099 100644
--- a/egg/tests/test-asn1.c
+++ b/egg/tests/test-asn1.c
@@ -114,8 +114,7 @@ test_null (void)
 
 	g_assert_cmpint (EGG_ASN1X_NULL, ==, egg_asn1x_type (asn));
 
-	if (!egg_asn1x_set_null (asn))
-		g_assert_not_reached ();
+	egg_asn1x_set_null (asn);
 
 	data = egg_asn1x_encode (asn, g_realloc);
 	egg_assert_cmpmem (NULL_TEST, XL (NULL_TEST), ==, g_bytes_get_data (data, NULL), g_bytes_get_size (data));
@@ -187,8 +186,7 @@ test_unsigned (void)
 
 	egg_asn1x_clear (asn);
 
-	if (!egg_asn1x_set_integer_as_ulong (asn, 253))
-		g_assert_not_reached ();
+	egg_asn1x_set_integer_as_ulong (asn, 253);
 
 	check = egg_asn1x_encode (asn, NULL);
 	egg_assert_cmpmem (I253, XL (I253), ==, g_bytes_get_data (check, NULL), g_bytes_get_size (check));
@@ -533,9 +531,7 @@ test_bit_string_encode_decode_ulong (void)
 	asn = egg_asn1x_create (test_asn1_tab, "TestBitString");
 	g_assert (asn);
 
-	if (!egg_asn1x_set_bits_as_ulong (asn, bits, n_bits))
-		g_assert_not_reached ();
-
+	egg_asn1x_set_bits_as_ulong (asn, bits, n_bits);
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data);
 
@@ -584,10 +580,9 @@ test_have (void)
 
 	g_assert (!egg_asn1x_have (asn));
 
-	if (!egg_asn1x_set_boolean (asn, TRUE))
-		g_assert_not_reached ();
+	egg_asn1x_set_boolean (asn, TRUE);
 
-	g_assert (!egg_asn1x_have (asn));
+	g_assert (egg_asn1x_have (asn));
 
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data);
@@ -627,7 +622,7 @@ test_any_set_raw (void)
 
 	bytes = g_bytes_new_with_free_func (SFARNSWORTH, XL (SFARNSWORTH),
 	                                      test_is_freed, NULL);
-	if (!egg_asn1x_set_element_raw (node, bytes))
+	if (!egg_asn1x_set_any_raw (node, bytes))
 		g_assert_not_reached ();
 	g_bytes_unref (bytes);
 
@@ -653,7 +648,6 @@ test_any_set_raw_explicit (void)
 	GBytes *bytes;
 	GNode *asn, *node;
 	GBytes *data;
-	GBytes *check;
 
 	/* ENCODED SEQUENCE [89] ANY with OCTET STRING */
 	const gchar SEQ_ENCODING[] =  "\x30\x0F\xBF\x59\x0C\x04\x0A""farnsworth";
@@ -666,7 +660,7 @@ test_any_set_raw_explicit (void)
 	g_assert (node);
 
 	bytes = g_bytes_new_with_free_func (SFARNSWORTH, XL (SFARNSWORTH), test_is_freed, NULL);
-	if (!egg_asn1x_set_element_raw (node, bytes))
+	if (!egg_asn1x_set_any_raw (node, bytes))
 		g_assert_not_reached ();
 	g_bytes_unref (bytes);
 
@@ -675,13 +669,7 @@ test_any_set_raw_explicit (void)
 
 	egg_assert_cmpbytes (data, ==, SEQ_ENCODING, XL (SEQ_ENCODING));
 
-	check = egg_asn1x_get_element_raw (node);
-	g_assert (check);
-
-	egg_assert_cmpbytes (check, ==, SFARNSWORTH, XL (SFARNSWORTH));
-
 	g_bytes_unref (data);
-	g_bytes_unref (check);
 	egg_asn1x_destroy (asn);
 	g_assert (is_freed);
 }
@@ -702,7 +690,7 @@ test_choice_not_chosen (void)
 	g_assert (node);
 
 	bytes = g_bytes_new_static (SFARNSWORTH, XL (SFARNSWORTH));
-	if (!egg_asn1x_set_element_raw (node, bytes))
+	if (!egg_asn1x_set_any_raw (node, bytes))
 		g_assert_not_reached ();
 	g_bytes_unref (bytes);
 
@@ -736,7 +724,7 @@ perform_asn1_any_choice_set_raw (const gchar *choice, const gchar *encoding, gsi
 		g_assert_not_reached ();
 
 	bytes = g_bytes_new_with_free_func (SFARNSWORTH, XL (SFARNSWORTH), test_is_freed, NULL);
-	if (!egg_asn1x_set_element_raw (node, bytes))
+	if (!egg_asn1x_set_any_raw (node, bytes))
 		g_assert_not_reached ();
 	g_bytes_unref (bytes);
 
@@ -799,8 +787,7 @@ test_append (void)
 	g_assert (child);
 
 	/* Second integer is 2 */
-	if (!egg_asn1x_set_integer_as_ulong (child, 2))
-		g_assert_not_reached ();
+	egg_asn1x_set_integer_as_ulong (child, 2);
 
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data != NULL);
@@ -822,12 +809,10 @@ test_append_and_clear (void)
 
 	g_assert_cmpuint (egg_asn1x_count (asn), ==, 0);
 
-	if (!egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 2))
-		g_assert_not_reached ();
-	if (!egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 3))
-		g_assert_not_reached ();
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 2);
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 3);
 
-	g_assert_cmpuint (egg_asn1x_count (asn), ==, 0);
+	g_assert_cmpuint (egg_asn1x_count (asn), ==, 2);
 
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data != NULL);
@@ -862,12 +847,10 @@ test_setof (void)
 	g_assert_cmpint (EGG_ASN1X_SET_OF, ==, egg_asn1x_type (asn));
 
 	/* Add integer 1, in SET OF DER should sort to front */
-	if (!egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 1))
-		g_assert_not_reached ();
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 1);
 
 	/* Add integer 8, in SET OF DER should sort to back */
-	if (!egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 8))
-		g_assert_not_reached ();
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_append (asn), 8);
 
 	data = egg_asn1x_encode (asn, NULL);
 	if (data == NULL) {
@@ -924,8 +907,7 @@ test_enumerated (void)
 	g_assert (value);
 	g_assert_cmpstr (g_quark_to_string (value), ==, "valueTwo");
 
-	if (!egg_asn1x_set_enumerated (asn, g_quark_from_static_string ("valueThree")))
-		g_assert_not_reached ();
+	egg_asn1x_set_enumerated (asn, g_quark_from_static_string ("valueThree"));
 
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data != NULL);
@@ -984,14 +966,9 @@ test_asn1_integers (Test* test, gconstpointer unused)
 	asn = egg_asn1x_create (test_asn1_tab, "TestIntegers");
 	g_assert ("asn test structure is null" && asn != NULL);
 
-	ret = egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "uint1", NULL), 35);
-	g_assert ("couldn't write integer" && ret);
-
-	ret = egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "uint2", NULL), 23456);
-	g_assert ("couldn't write integer" && ret);
-
-	ret = egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "uint3", NULL), 209384022);
-	g_assert ("couldn't write integer" && ret);
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "uint1", NULL), 35);
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "uint2", NULL), 23456);
+	egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "uint3", NULL), 209384022);
 
 	/* Now encode the whole caboodle */
 	data = egg_asn1x_encode (asn, NULL);
@@ -1027,6 +1004,10 @@ test_boolean_seq (Test* test, gconstpointer unused)
 	GNode *asn = NULL;
 	gboolean value, ret;
 
+	/* The first boolean has a default of FALSE, so doesn't get encoded if FALSE */
+	const gchar SEQ_BOOLEAN_TRUE_FALSE[] = "\x30\x06\x01\x01\xFF\x01\x01\x00";
+	const gchar SEQ_BOOLEAN_FALSE_FALSE[] = "\x30\x03\x01\x01\x00";
+
 	asn = egg_asn1x_create (test_asn1_tab, "TestBooleanSeq");
 	g_assert ("asn test structure is null" && asn != NULL);
 
@@ -1036,22 +1017,23 @@ test_boolean_seq (Test* test, gconstpointer unused)
 	g_assert (ret == TRUE);
 	g_assert (value == FALSE);
 
-	ret = egg_asn1x_set_boolean (egg_asn1x_node (asn, "boolean", NULL), TRUE);
-	g_assert (ret == TRUE);
+	egg_asn1x_set_boolean (egg_asn1x_node (asn, "boolean", NULL), TRUE);
+	egg_asn1x_set_boolean (egg_asn1x_node (asn, "boolean2", NULL), FALSE);
 
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data != NULL);
+	egg_assert_cmpbytes (data, ==, SEQ_BOOLEAN_TRUE_FALSE, XL (SEQ_BOOLEAN_TRUE_FALSE));
+	g_bytes_unref (data);
 
 	ret = egg_asn1x_get_boolean (egg_asn1x_node (asn, "boolean", NULL), &value);
 	g_assert (ret);
 	g_assert (value == TRUE);
 
-	ret = egg_asn1x_set_boolean (egg_asn1x_node (asn, "boolean", NULL), FALSE);
-	g_assert (ret == TRUE);
+	egg_asn1x_set_boolean (egg_asn1x_node (asn, "boolean", NULL), FALSE);
 
-	g_bytes_unref (data);
 	data = egg_asn1x_encode (asn, NULL);
 	g_assert (data != NULL);
+	egg_assert_cmpbytes (data, ==, SEQ_BOOLEAN_FALSE_FALSE, XL (SEQ_BOOLEAN_FALSE_FALSE));
 
 	ret = egg_asn1x_get_boolean (egg_asn1x_node (asn, "boolean", NULL), &value);
 	g_assert (ret);
@@ -1072,8 +1054,7 @@ test_write_value (Test* test, gconstpointer unused)
 	asn = egg_asn1x_create (test_asn1_tab, "TestData");
 	g_assert ("asn test structure is null" && asn != NULL);
 
-	if (!egg_asn1x_set_string_as_raw (egg_asn1x_node (asn, "data", NULL), (guchar*)"SOME DATA", 9, NULL))
-		g_assert_not_reached ();
+	egg_asn1x_set_string_as_raw (egg_asn1x_node (asn, "data", NULL), (guchar*)"SOME DATA", 9, NULL);
 
 	encoded = egg_asn1x_encode (asn, NULL);
 	g_assert (encoded);
@@ -1100,8 +1081,7 @@ test_element_length_content (Test* test, gconstpointer unused)
 	asn = egg_asn1x_create (test_asn1_tab, "TestData");
 	g_assert ("asn test structure is null" && asn != NULL);
 
-	if (!egg_asn1x_set_string_as_raw (egg_asn1x_node (asn, "data", NULL), (guchar*)"SOME DATA", 9, NULL))
-		g_assert_not_reached ();
+	egg_asn1x_set_string_as_raw (egg_asn1x_node (asn, "data", NULL), (guchar*)"SOME DATA", 9, NULL);
 
 	buffer = egg_asn1x_encode (asn, NULL);
 	g_assert (buffer != NULL);
@@ -1143,19 +1123,22 @@ test_read_element (Test* test, gconstpointer unused)
 	asn = egg_asn1x_create (test_asn1_tab, "TestData");
 	g_assert ("asn test structure is null" && asn != NULL);
 
-	if (!egg_asn1x_set_string_as_raw (egg_asn1x_node (asn, "data", NULL), (guchar*)"SOME DATA", 9, NULL))
-		g_assert_not_reached ();
+	egg_asn1x_set_string_as_raw (egg_asn1x_node (asn, "data", NULL), (guchar*)"SOME DATA", 9, NULL);
 
 	buffer = egg_asn1x_encode (asn, NULL);
 	g_assert (buffer != NULL);
 
+	/* Have to decode before we can get raw elements */
+	if (!egg_asn1x_decode (asn, buffer))
+		g_assert_not_reached ();
+
 	/* Now the real test */
 	data = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "data", NULL));
 	g_assert (data != NULL);
 	g_assert_cmpint (g_bytes_get_size (data), ==, 11);
 	g_bytes_unref (data);
 
-	data = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "data", NULL));
+	data = egg_asn1x_get_value_raw (egg_asn1x_node (asn, "data", NULL));
 	g_assert (data != NULL);
 	egg_assert_cmpbytes (data, ==, "SOME DATA", 9);
 	g_bytes_unref (data);
diff --git a/egg/tests/test-asn1x.c b/egg/tests/test-asn1x.c
index 4581756..ad75e62 100644
--- a/egg/tests/test-asn1x.c
+++ b/egg/tests/test-asn1x.c
@@ -32,6 +32,7 @@
 #include <unistd.h>
 
 #if 0
+#include <libtasn1.h>
 static void
 build_personal_name (void)
 {
@@ -39,7 +40,7 @@ build_personal_name (void)
 	guchar buffer[10024];
 	int res, len;
 
-	res = asn1_array2tree (pkix_asn1_tab, &asn1_pkix, NULL);
+	res = asn1_array2tree ((ASN1_ARRAY_TYPE*)pkix_asn1_tab, &asn1_pkix, NULL);
 	g_assert (res == ASN1_SUCCESS);
 
 	res = asn1_create_element (asn1_pkix, "PKIX1.PersonalName", &asn);
@@ -48,7 +49,7 @@ build_personal_name (void)
 	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);
+	asn1_write_value (asn, "generation-qualifier", "II", 2);
 
 	len = sizeof (buffer);
 	res = asn1_der_coding (asn, "", buffer, &len, NULL);
@@ -63,55 +64,136 @@ build_personal_name (void)
 }
 #endif
 
+typedef struct {
+	GBytes *data;
+} Test;
+
+typedef struct {
+	const EggAsn1xDef *defs;
+	const gchar *filename;
+	const gchar *identifier;
+} Fixture;
+
+static const Fixture parse_test_fixtures[] = {
+	{ pkix_asn1_tab, SRCDIR "/files/test-certificate-1.der", "Certificate" },
+	{ pkix_asn1_tab, SRCDIR "/files/test-pkcs8-1.der", "pkcs-8-PrivateKeyInfo" },
+	{ pk_asn1_tab, SRCDIR "/files/test-rsakey-1.der", "RSAPrivateKey" },
+	{ pkix_asn1_tab, SRCDIR "/files/test-personalname-1.der", "PersonalName" },
+	{ pkix_asn1_tab, SRCDIR "/files/test-pkcs7-1.der", "pkcs-7-ContentInfo" },
+	{ pkix_asn1_tab, SRCDIR "/files/test-pkcs7-2.der", "pkcs-7-ContentInfo" },
+};
+
+static void
+setup (Test *test,
+       gconstpointer data)
+{
+	const gchar *filename = data;
+	GError *error = NULL;
+	gchar *contents;
+	gsize length;
+
+	g_file_get_contents (filename, (gchar**)&contents, &length, &error);
+	g_assert_no_error (error);
+
+	test->data = g_bytes_new_take (contents, length);
+}
+
+static void
+setup_parsing (Test *test,
+               gconstpointer data)
+{
+	const Fixture *fixture = data;
+	setup (test, fixture->filename);
+}
+
 static void
-test_some_asn1_stuff (const EggAsn1xDef *defs,
-                      const gchar *file,
-                      const gchar *identifier)
+teardown (Test *test,
+          gconstpointer unused)
 {
+	g_bytes_unref (test->data);
+}
+
+static void
+test_decode_encode (Test *test,
+                    gconstpointer data)
+{
+	const Fixture *fixture = data;
 	GNode *asn;
 	GBytes *encoded;
-	gpointer data;
-	gsize n_data;
-	GBytes *bytes;
 
-	if (!g_file_get_contents (file, (gchar**)&data, &n_data, NULL))
-		g_assert_not_reached ();
-	bytes = g_bytes_new_take (data, n_data);
-	asn = egg_asn1x_create (defs, identifier);
-	egg_asn1x_dump (asn);
+	asn = egg_asn1x_create (fixture->defs, fixture->identifier);
 
-	if (!egg_asn1x_decode (asn, bytes))
-		g_warning ("decode of %s failed: %s", identifier, egg_asn1x_message (asn));
+	if (g_test_verbose ())
+		egg_asn1x_dump (asn);
+
+	if (!egg_asn1x_decode (asn, test->data)) {
+		g_warning ("decode of %s failed: %s", fixture->identifier,
+		           egg_asn1x_message (asn));
+		g_assert_not_reached ();
+	}
 
 	encoded = egg_asn1x_encode (asn, NULL);
-	if (encoded == NULL)
-		g_warning ("encode of %s failed: %s", identifier, egg_asn1x_message (asn));
+	if (encoded == NULL) {
+		g_warning ("encode of %s failed: %s", fixture->identifier,
+		           egg_asn1x_message (asn));
+		g_assert_not_reached ();
+	}
 
 	/* Decode the encoding */
-	if (!egg_asn1x_decode (asn, encoded))
-		g_warning ("decode of encoded %s failed: %s", identifier, egg_asn1x_message (asn));
+	if (!egg_asn1x_decode (asn, encoded)) {
+		g_warning ("decode of encoded %s failed: %s", fixture->identifier,
+		           egg_asn1x_message (asn));
+		g_assert_not_reached ();
+	}
 
 	egg_asn1x_clear (asn);
 	egg_asn1x_destroy (asn);
-	g_bytes_unref (bytes);
 	g_bytes_unref (encoded);
 }
 
+static void
+test_pkcs12_decode (Test *test,
+                    gconstpointer unused)
+{
+	GNode *asn;
+
+	asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-12-PFX");
+
+	if (g_test_verbose ())
+		egg_asn1x_dump (asn);
+
+	if (!egg_asn1x_decode (asn, test->data)) {
+		g_warning ("decode of indefinite pkcs-12-PFX failed: %s",
+		           egg_asn1x_message (asn));
+		g_assert_not_reached ();
+	}
+
+	egg_asn1x_destroy (asn);
+}
+
 int
 main (int argc, char **argv)
 {
-	/* Build up a personal name, which is a set */
+	gchar *name;
+	gint i;
+
+	g_test_init (&argc, &argv, NULL);
+
 #if 0
+	/* Build up a personal name, which is a set */
 	build_personal_name ();
 #endif
 
-	test_some_asn1_stuff (pkix_asn1_tab, SRCDIR "/files/test-certificate-1.der", "Certificate");
-	test_some_asn1_stuff (pkix_asn1_tab, SRCDIR "/files/test-pkcs8-1.der", "pkcs-8-PrivateKeyInfo");
-	test_some_asn1_stuff (pk_asn1_tab, SRCDIR "/files/test-rsakey-1.der", "RSAPrivateKey");
-	test_some_asn1_stuff (pkix_asn1_tab, SRCDIR "/files/test-personalname-1.der", "PersonalName");
-	test_some_asn1_stuff (pkix_asn1_tab, SRCDIR "/files/test-pkcs7-1.der", "pkcs-7-ContentInfo");
-	test_some_asn1_stuff (pkix_asn1_tab, SRCDIR "/files/test-pkcs7-2.der", "pkcs-7-ContentInfo");
-	test_some_asn1_stuff (pkix_asn1_tab, SRCDIR "/files/test-pkcs12-1.der", "pkcs-12-PFX");
+	for (i = 0; i < G_N_ELEMENTS (parse_test_fixtures); i++) {
+		name = g_strdup_printf ("/asn1x/encode-decode-%s", parse_test_fixtures[i].identifier);
+		g_test_add (name, Test, &parse_test_fixtures[i], setup_parsing, test_decode_encode, teardown);
+		g_free (name);
+	}
+
+	g_test_add ("/asn1x/pkcs12-decode/1", Test, SRCDIR "/files/test-pkcs12-1.der",
+	            setup, test_pkcs12_decode, teardown);
+	g_test_add ("/asn1x/pkcs12-decode/2", Test, SRCDIR "/files/test-pkcs12-2.der",
+	            setup, test_pkcs12_decode, teardown);
 
-	return 0;
+	return g_test_run ();
 }
diff --git a/egg/tests/test-dn.c b/egg/tests/test-dn.c
index cf83dcb..6494679 100644
--- a/egg/tests/test-dn.c
+++ b/egg/tests/test-dn.c
@@ -85,24 +85,30 @@ test_dn_value (Test* test, gconstpointer unused)
 	const guchar value[] = { 0x13, 0x1a, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20, 0x43, 0x41 };
 	gsize n_value = 28;
 	GBytes *bytes;
+	GNode *asn;
 	GQuark oid;
 	gchar *text;
 
+	bytes = g_bytes_new_static (value, n_value);
+
+	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "AttributeValue", bytes);
+	g_assert (asn != NULL);
+
 	/* Some printable strings */
 	oid = g_quark_from_static_string ("2.5.4.3");
-	bytes = g_bytes_new_static (value, n_value);
-	text = egg_dn_print_value (oid, bytes);
-	g_bytes_unref (bytes);
+	text = egg_dn_print_value (oid, asn);
 	g_assert_cmpstr (text, ==, "Thawte Personal Premium CA");
 	g_free (text);
 
 	/* Unknown oid */
 	oid = g_quark_from_static_string ("1.1.1.1.1.1");
 	bytes = g_bytes_new_static (value, n_value);
-	text = egg_dn_print_value (oid, bytes);
-	g_bytes_unref (bytes);
+	text = egg_dn_print_value (oid, asn);
 	g_assert_cmpstr (text, ==, "#131A54686177746520506572736F6E616C205072656D69756D204341");
 	g_free (text);
+
+	egg_asn1x_destroy (asn);
+	g_bytes_unref (bytes);
 }
 
 static int last_index = 0;
@@ -110,7 +116,7 @@ static int last_index = 0;
 static void
 concatenate_dn (guint index,
                 GQuark oid,
-                GBytes *value,
+                GNode *value,
                 gpointer user_data)
 {
 	GString *dn = user_data;
@@ -118,7 +124,6 @@ concatenate_dn (guint index,
 
 	g_assert (oid);
 	g_assert (value != NULL);
-	g_assert (g_bytes_get_size (value) != 0);
 
 	g_assert (index == last_index);
 	++last_index;
diff --git a/egg/tests/test.asn b/egg/tests/test.asn
index 5412a63..0465328 100644
--- a/egg/tests/test.asn
+++ b/egg/tests/test.asn
@@ -33,7 +33,8 @@ TestData ::= SEQUENCE {
 }
 
 TestBooleanSeq ::= SEQUENCE {
-	boolean                 BOOLEAN DEFAULT FALSE
+	boolean                 BOOLEAN DEFAULT FALSE,
+	boolean2                BOOLEAN
 }
 
 TestOid ::= SEQUENCE {
diff --git a/gcr/gcr-certificate-extensions.c b/gcr/gcr-certificate-extensions.c
index ba16907..6b8033c 100644
--- a/gcr/gcr-certificate-extensions.c
+++ b/gcr/gcr-certificate-extensions.c
@@ -56,7 +56,7 @@ _gcr_certificate_extension_find (GNode *cert,
 			}
 
 			/* Extension value */
-			return egg_asn1x_get_raw_value (egg_asn1x_node (node, "extnValue", NULL));
+			return egg_asn1x_get_string_as_bytes (egg_asn1x_node (node, "extnValue", NULL));
 		}
 	}
 
@@ -172,28 +172,28 @@ general_name_parse_other (GNode *node, GcrGeneralName *general)
 {
 	GNode *decode = NULL;
 	GQuark oid;
-	GBytes *value;
+	GNode *any;
 
 	general->type = GCR_GENERAL_NAME_OTHER;
 	general->description = _("Other Name");
+	general->display = NULL;
 
 	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "type-id", NULL));
-	value = egg_asn1x_get_element_raw (egg_asn1x_node (node, "value", NULL));
+	any = egg_asn1x_node (node, "value", NULL);
 
-	if (value == NULL)
+	if (any == NULL)
 		return;
 
 	if (oid == GCR_OID_ALT_NAME_XMPP_ADDR) {
 		general->description = _("XMPP Addr");
-		decode = egg_asn1x_create_and_decode (pkix_asn1_tab, "UTF8String", value);
+		decode = egg_asn1x_get_any_as (any, pkix_asn1_tab, "UTF8String");
 		general->display = egg_asn1x_get_string_as_utf8 (decode, g_realloc);
 	} else if (oid == GCR_OID_ALT_NAME_DNS_SRV) {
 		general->description = _("DNS SRV");
-		decode = egg_asn1x_create_and_decode (pkix_asn1_tab, "IA5String", value);
+		decode = egg_asn1x_get_any_as (any, pkix_asn1_tab, "IA5String");
 		general->display = egg_asn1x_get_string_as_utf8 (decode, g_realloc);
 	}
 
-	g_bytes_unref (value);
 	egg_asn1x_destroy (decode);
 }
 
diff --git a/gcr/gcr-certificate-renderer.c b/gcr/gcr-certificate-renderer.c
index 631267c..d4e05f4 100644
--- a/gcr/gcr-certificate-renderer.c
+++ b/gcr/gcr-certificate-renderer.c
@@ -789,7 +789,7 @@ typedef struct {
 static void
 on_parsed_dn_part (guint index,
                    GQuark oid,
-                   GBytes *value,
+                   GNode *value,
                    gpointer user_data)
 {
 	GcrRenderer *renderer = ((AppendDnClosure *)user_data)->renderer;
@@ -935,7 +935,7 @@ _gcr_certificate_renderer_append_extension (GcrRenderer *renderer,
 	g_return_if_fail (oid);
 
 	/* Extension value */
-	value = egg_asn1x_get_raw_value (egg_asn1x_node (node, "extnValue", NULL));
+	value = egg_asn1x_get_string_as_bytes (egg_asn1x_node (node, "extnValue", NULL));
 
 	/* The custom parsers */
 	if (oid == GCR_OID_BASIC_CONSTRAINTS)
diff --git a/gcr/gcr-certificate-request.c b/gcr/gcr-certificate-request.c
index 81a510a..686ed06 100644
--- a/gcr/gcr-certificate-request.c
+++ b/gcr/gcr-certificate-request.c
@@ -420,7 +420,7 @@ prepare_subject_public_key_and_mechanisms (GcrCertificateRequest *self,
 	}
 
 	node = egg_asn1x_node (self->asn, "certificationRequestInfo", "subjectPKInfo", NULL);
-	if (!egg_asn1x_set_element_raw (node, encoded))
+	if (!egg_asn1x_decode (node, encoded))
 		g_return_val_if_reached (FALSE);
 
 	g_bytes_unref (encoded);
@@ -434,7 +434,6 @@ encode_take_signature_into_request (GcrCertificateRequest *self,
                                     guchar *result,
                                     gsize n_result)
 {
-	GBytes *data;
 	GNode *params;
 	GNode *node;
 
@@ -446,9 +445,7 @@ encode_take_signature_into_request (GcrCertificateRequest *self,
 
 	node = egg_asn1x_node (self->asn, "signatureAlgorithm", "parameters", NULL);
 	params = egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL);
-	data = egg_asn1x_encode (params, NULL);
-	egg_asn1x_set_element_raw (node, data);
-	g_bytes_unref (data);
+	egg_asn1x_set_any_from (node, params);
 }
 
 /**
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 0dc32b7..b617a24 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -566,7 +566,7 @@ done:
 static gint
 parse_der_private_key_dsa_parts (GcrParser *self,
                                  GBytes *keydata,
-                                 GBytes *params)
+                                 GNode *params)
 {
 	gint ret = GCR_ERROR_UNRECOGNIZED;
 	GNode *asn_params = NULL;
@@ -575,7 +575,7 @@ parse_der_private_key_dsa_parts (GcrParser *self,
 
 	parsed = push_parsed (self, TRUE);
 
-	asn_params = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", params);
+	asn_params = egg_asn1x_get_any_as (params, pk_asn1_tab, "DSAParameters");
 	asn_key = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPrivatePart", keydata);
 	if (!asn_params || !asn_key)
 		goto done;
@@ -629,7 +629,7 @@ static gint
 handle_subject_public_key_rsa (GcrParser *self,
                                GcrParsed *parsed,
                                GBytes *key,
-                               GBytes *params)
+                               GNode *params)
 {
 	gint res = GCR_ERROR_FAILURE;
 	GNode *asn = NULL;
@@ -656,14 +656,14 @@ static gint
 handle_subject_public_key_dsa (GcrParser *self,
                                GcrParsed *parsed,
                                GBytes *key,
-                               GBytes *params)
+                               GNode *params)
 {
 	gint res = GCR_ERROR_FAILURE;
 	GNode *key_asn = NULL;
 	GNode *param_asn = NULL;
 
 	key_asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPublicPart", key);
-	param_asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", params);
+	param_asn = egg_asn1x_get_any_as (params, pk_asn1_tab, "DSAParameters");
 
 	if (!key_asn || !param_asn)
 		goto done;
@@ -690,7 +690,7 @@ parse_der_subject_public_key (GcrParser *self,
                               GBytes *data)
 {
 	GcrParsed *parsed;
-	GBytes *params;
+	GNode *params;
 	GBytes *key;
 	GNode *asn = NULL;
 	GNode *node;
@@ -708,8 +708,7 @@ parse_der_subject_public_key (GcrParser *self,
 	node = egg_asn1x_node (asn, "algorithm", "algorithm", NULL);
 	oid = egg_asn1x_get_oid_as_quark (node);
 
-	node = egg_asn1x_node (asn, "algorithm", "parameters", NULL);
-	params = egg_asn1x_get_element_raw (node);
+	params = egg_asn1x_node (asn, "algorithm", "parameters", NULL);
 
 	node = egg_asn1x_node (asn, "subjectPublicKey", NULL);
 	key = egg_asn1x_get_bits_as_raw (node, &bits);
@@ -724,7 +723,6 @@ parse_der_subject_public_key (GcrParser *self,
 		ret = GCR_ERROR_UNRECOGNIZED;
 
 	g_bytes_unref (key);
-	g_bytes_unref (params);
 
 	if (ret == SUCCESS)
 		parsed_fire (self, parsed);
@@ -747,7 +745,7 @@ parse_der_pkcs8_plain (GcrParser *self,
 	CK_KEY_TYPE key_type;
 	GQuark key_algo;
 	GBytes *keydata = NULL;
-	GBytes *params = NULL;
+	GNode *params = NULL;
 	GNode *asn = NULL;
 	GcrParsed *parsed;
 
@@ -775,11 +773,11 @@ parse_der_pkcs8_plain (GcrParser *self,
   		goto done;
   	}
 
-	keydata = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "privateKey", NULL));
+	keydata = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "privateKey", NULL));
 	if (!keydata)
 		goto done;
 
-	params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "privateKeyAlgorithm", "parameters", NULL));
+	params = egg_asn1x_node (asn, "privateKeyAlgorithm", "parameters", NULL);
 
 	ret = SUCCESS;
 
@@ -809,8 +807,6 @@ done:
 
 	if (keydata)
 		g_bytes_unref (keydata);
-	if (params)
-		g_bytes_unref (params);
 	egg_asn1x_destroy (asn);
 	pop_parsed (self, parsed);
 	return ret;
@@ -827,7 +823,7 @@ parse_der_pkcs8_encrypted (GcrParser *self,
 	gint ret, r;
 	GQuark scheme;
 	guchar *crypted = NULL;
-	GBytes *params = NULL;
+	GNode *params = NULL;
 	GBytes *cbytes;
 	gsize n_crypted;
 	const gchar *password;
@@ -849,7 +845,7 @@ parse_der_pkcs8_encrypted (GcrParser *self,
 	if (!scheme)
 		goto done;
 
-	params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "encryptionAlgorithm", "parameters", NULL));
+	params = egg_asn1x_node (asn, "encryptionAlgorithm", "parameters", NULL);
 
 	/* Loop to try different passwords */
 	for (;;) {
@@ -901,8 +897,6 @@ parse_der_pkcs8_encrypted (GcrParser *self,
 	}
 
 done:
-	if (params)
-		g_bytes_unref (params);
 	if (cih)
 		gcry_cipher_close (cih);
 	egg_asn1x_destroy (asn);
@@ -977,7 +971,7 @@ parse_der_certificate (GcrParser *self,
 
 static gint
 handle_pkcs7_signed_data (GcrParser *self,
-                          GBytes *data)
+                          GNode *content)
 {
 	GNode *asn = NULL;
 	GNode *node;
@@ -987,7 +981,7 @@ handle_pkcs7_signed_data (GcrParser *self,
 
 	ret = GCR_ERROR_UNRECOGNIZED;
 
-	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-7-SignedData", data);
+	asn = egg_asn1x_get_any_as (content, pkix_asn1_tab, "pkcs-7-SignedData");
 	if (!asn)
 		goto done;
 
@@ -1023,7 +1017,7 @@ parse_der_pkcs7 (GcrParser *self,
 	GNode *asn = NULL;
 	GNode *node;
 	gint ret;
-	GBytes *content = NULL;
+	GNode *content = NULL;
 	GQuark oid;
 	GcrParsed *parsed;
 
@@ -1050,15 +1044,13 @@ parse_der_pkcs7 (GcrParser *self,
 		goto done;
 	}
 
-	content = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "content", NULL));
+	content = egg_asn1x_node (asn, "content", NULL);
 	if (!content)
 		goto done;
 
 	ret = handle_pkcs7_signed_data (self, content);
 
 done:
-	if (content)
-		g_bytes_unref (content);
 	egg_asn1x_destroy (asn);
 	pop_parsed (self, parsed);
 	return ret;
@@ -1068,37 +1060,6 @@ done:
  * PKCS12
  */
 
-static GNode *
-decode_pkcs12_asn1_accepting_invalid_crap (const EggAsn1xDef *defs,
-                                           const gchar *identifier,
-                                           GBytes *data)
-{
-	GNode *asn;
-
-	/*
-	 * Because PKCS#12 files, the bags specifically, are notorious for
-	 * being crappily constructed and are often break rules such as DER
-	 * sorting order etc.. we parse the DER in a non-strict fashion.
-	 *
-	 * The rules in DER are designed for X.509 certificates, so there is
-	 * only one way to represent a given certificate (although they fail
-	 * at that as well). But with PKCS#12 we don't have such high
-	 * requirements, and we can slack off on our validation.
-	 */
-
-	asn = egg_asn1x_create (defs, identifier);
-	g_return_val_if_fail (asn != NULL, NULL);
-
-	/* Passing FALSE as the strictness argument */
-	if (!egg_asn1x_decode_no_validate (asn, data) ||
-	    !egg_asn1x_validate (asn, FALSE)) {
-		egg_asn1x_destroy (asn);
-		asn = NULL;
-	}
-
-	return asn;
-}
-
 static gint
 handle_pkcs12_cert_bag (GcrParser *self,
                         GBytes *data)
@@ -1106,25 +1067,24 @@ handle_pkcs12_cert_bag (GcrParser *self,
 	GNode *asn = NULL;
 	GNode *asn_content = NULL;
 	guchar *certificate = NULL;
-	GBytes *element = NULL;
+	GNode *element = NULL;
 	gsize n_certificate;
 	GBytes *bytes;
 	gint ret;
 
 	ret = GCR_ERROR_UNRECOGNIZED;
-	asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
-	                                                 "pkcs-12-CertBag",
-	                                                 data);
+	asn = egg_asn1x_create_and_decode_full (pkix_asn1_tab, "pkcs-12-CertBag",
+	                                        data, EGG_ASN1X_NO_STRICT);
 	if (!asn)
 		goto done;
 
 	ret = GCR_ERROR_FAILURE;
 
-	element = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "certValue", NULL));
+	element = egg_asn1x_node (asn, "certValue", NULL);
 	if (!element)
 		goto done;
 
-	asn_content = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-7-Data", element);
+	asn_content = egg_asn1x_get_any_as (element, pkix_asn1_tab, "pkcs-7-Data");
 	if (!asn_content)
 		goto done;
 
@@ -1137,8 +1097,6 @@ handle_pkcs12_cert_bag (GcrParser *self,
 	g_bytes_unref (bytes);
 
 done:
-	if (element)
-		g_bytes_unref (element);
 	egg_asn1x_destroy (asn_content);
 	egg_asn1x_destroy (asn);
 	return ret;
@@ -1150,7 +1108,6 @@ parse_pkcs12_bag_friendly_name (GNode *asn)
 	guint count, i;
 	GQuark oid;
 	GNode *node;
-	GBytes *element;
 	GNode *asn_str;
 	gchar *result;
 
@@ -1163,10 +1120,7 @@ parse_pkcs12_bag_friendly_name (GNode *asn)
 		if (oid == GCR_OID_PKCS9_ATTRIBUTE_FRIENDLY) {
 			node = egg_asn1x_node (asn, i, "values", 1, NULL);
 			if (node != NULL) {
-				element = egg_asn1x_get_element_raw (node);
-				asn_str = egg_asn1x_create_and_decode (pkix_asn1_tab, "BMPString",
-				                                       element);
-				g_bytes_unref (element);
+				asn_str = egg_asn1x_get_any_as (node, pkix_asn1_tab, "BMPString");
 				if (asn_str) {
 					result = egg_asn1x_get_bmpstring_as_utf8 (asn_str);
 					egg_asn1x_destroy (asn_str);
@@ -1187,6 +1141,7 @@ handle_pkcs12_bag (GcrParser *self,
 	gint ret, r;
 	guint count = 0;
 	GQuark oid;
+	GNode *value;
 	GBytes *element = NULL;
 	gchar *friendly;
 	guint i;
@@ -1194,9 +1149,8 @@ handle_pkcs12_bag (GcrParser *self,
 
 	ret = GCR_ERROR_UNRECOGNIZED;
 
-	asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
-	                                                 "pkcs-12-SafeContents",
-	                                                 data);
+	asn = egg_asn1x_create_and_decode_full (pkix_asn1_tab, "pkcs-12-SafeContents",
+	                                        data, EGG_ASN1X_NO_STRICT);
 	if (!asn)
 		goto done;
 
@@ -1215,10 +1169,11 @@ handle_pkcs12_bag (GcrParser *self,
 		if (!oid)
 			goto done;
 
-		element = egg_asn1x_get_element_raw (egg_asn1x_node (asn, i, "bagValue", NULL));
-		if (!element)
+		value = egg_asn1x_node (asn, i, "bagValue", NULL);
+		if (!value)
 			goto done;
 
+		element = egg_asn1x_get_element_raw (value);
 		parsed = push_parsed (self, FALSE);
 
 		friendly = parse_pkcs12_bag_friendly_name (egg_asn1x_node (asn, i, "bagAttributes", NULL));
@@ -1266,14 +1221,14 @@ done:
 
 static gint
 handle_pkcs12_encrypted_bag (GcrParser *self,
-                             GBytes *data)
+                             GNode *bag)
 {
 	PasswordState pstate = PASSWORD_STATE_INIT;
 	GNode *asn = NULL;
 	gcry_cipher_hd_t cih = NULL;
 	gcry_error_t gcry;
 	guchar *crypted = NULL;
-	GBytes *params = NULL;
+	GNode *params = NULL;
 	gsize n_crypted;
 	const gchar *password;
 	GBytes *cbytes;
@@ -1283,9 +1238,8 @@ handle_pkcs12_encrypted_bag (GcrParser *self,
 
 	ret = GCR_ERROR_UNRECOGNIZED;
 
-	asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
-	                                                 "pkcs-7-EncryptedData",
-	                                                 data);
+	asn = egg_asn1x_get_any_as_full (bag, pkix_asn1_tab, "pkcs-7-EncryptedData",
+	                                 EGG_ASN1X_NO_STRICT);
 	if (!asn)
 		goto done;
 
@@ -1296,7 +1250,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self,
 	if (!scheme)
 		goto done;
 
-	params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "encryptedContentInfo", "contentEncryptionAlgorithm", "parameters", NULL));
+	params = egg_asn1x_node (asn, "encryptedContentInfo", "contentEncryptionAlgorithm", "parameters", NULL);
 	if (!params)
 		goto done;
 
@@ -1352,8 +1306,6 @@ handle_pkcs12_encrypted_bag (GcrParser *self,
 	}
 
 done:
-	if (params)
-		g_bytes_unref (params);
 	if (cih)
 		gcry_cipher_close (cih);
 	egg_asn1x_destroy (asn);
@@ -1368,7 +1320,7 @@ handle_pkcs12_safe (GcrParser *self,
 	GNode *asn = NULL;
 	GNode *asn_content = NULL;
 	gint ret, r;
-	GBytes *bag = NULL;
+	GNode *bag;
 	GBytes *content;
 	GQuark oid;
 	guint i;
@@ -1376,9 +1328,8 @@ handle_pkcs12_safe (GcrParser *self,
 
 	ret = GCR_ERROR_UNRECOGNIZED;
 
-	asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
-	                                                 "pkcs-12-AuthenticatedSafe",
-	                                                 data);
+	asn = egg_asn1x_create_and_decode_full (pkix_asn1_tab, "pkcs-12-AuthenticatedSafe",
+	                                        data, EGG_ASN1X_NO_STRICT);
 	if (!asn)
 		goto done;
 
@@ -1396,20 +1347,16 @@ handle_pkcs12_safe (GcrParser *self,
 
 		oid = egg_asn1x_get_oid_as_quark (node);
 
-		node = egg_asn1x_node (asn, i + 1, "content", NULL);
-		if (!node)
+		bag = egg_asn1x_node (asn, i + 1, "content", NULL);
+		if (!bag)
 			goto done;
 
-		bag = egg_asn1x_get_element_raw (node);
-		g_return_val_if_fail (bag != NULL, ret);
-
 		/* A non encrypted bag, just parse */
 		if (oid == GCR_OID_PKCS7_DATA) {
 
 			egg_asn1x_destroy (asn_content);
-			asn_content = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
-			                                                         "pkcs-7-Data",
-			                                                         bag);
+			asn_content = egg_asn1x_get_any_as_full (bag, pkix_asn1_tab,
+			                                         "pkcs-7-Data", EGG_ASN1X_NO_STRICT);
 			if (!asn_content)
 				goto done;
 
@@ -1430,9 +1377,6 @@ handle_pkcs12_safe (GcrParser *self,
 			r = GCR_ERROR_UNRECOGNIZED;
 		}
 
-		g_bytes_unref (bag);
-		bag = NULL;
-
 		if (r == GCR_ERROR_FAILURE ||
 		    r == GCR_ERROR_CANCELLED ||
 		    r == GCR_ERROR_LOCKED) {
@@ -1444,8 +1388,6 @@ handle_pkcs12_safe (GcrParser *self,
 	ret = SUCCESS;
 
 done:
-	if (bag != NULL)
-		g_bytes_unref (bag);
 	egg_asn1x_destroy (asn);
 	egg_asn1x_destroy (asn_content);
 	return ret;
@@ -1465,7 +1407,6 @@ verify_pkcs12_safe (GcrParser *self,
 	gsize n_digest;
 	GQuark algorithm;
 	GNode *mac_data;
-	GBytes *params = NULL;
 	int ret, r;
 
 	ret = GCR_ERROR_FAILURE;
@@ -1484,10 +1425,6 @@ verify_pkcs12_safe (GcrParser *self,
 	if (!algorithm)
 		goto done;
 
-	params = egg_asn1x_get_element_raw (mac_data);
-	if (!params)
-		goto done;
-
 	digest = egg_asn1x_get_string_as_raw (egg_asn1x_node (mac_data, "mac", "digest", NULL), NULL, &n_digest);
 	if (!digest)
 		goto done;
@@ -1503,7 +1440,7 @@ verify_pkcs12_safe (GcrParser *self,
 		}
 
 		/* Parse the encryption stuff into a cipher. */
-		if (!egg_symkey_read_mac (algorithm, password, -1, params, &mdh, &mac_len)) {
+		if (!egg_symkey_read_mac (algorithm, password, -1, mac_data, &mdh, &mac_len)) {
 			ret = GCR_ERROR_FAILURE;
 			goto done;
 		}
@@ -1529,8 +1466,6 @@ verify_pkcs12_safe (GcrParser *self,
 	}
 
 done:
-	if (params)
-		g_bytes_unref (params);
 	if (mdh)
 		gcry_md_close (mdh);
 	g_free (digest);
@@ -1543,17 +1478,28 @@ parse_der_pkcs12 (GcrParser *self,
                   GBytes *data)
 {
 	GNode *asn = NULL;
-	GNode *asn_content = NULL;
 	gint ret;
-	GBytes *element = NULL;
-	GBytes *content = NULL;
+	GNode *content = NULL;
+	GBytes *string = NULL;
 	GQuark oid;
 	GcrParsed *parsed;
 
 	parsed = push_parsed (self, FALSE);
 	ret = GCR_ERROR_UNRECOGNIZED;
 
-	asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab, "pkcs-12-PFX", data);
+	/*
+	 * Because PKCS#12 files, the bags specifically, are notorious for
+	 * being crappily constructed and are often break rules such as DER
+	 * sorting order etc.. we parse the DER in a non-strict fashion.
+	 *
+	 * The rules in DER are designed for X.509 certificates, so there is
+	 * only one way to represent a given certificate (although they fail
+	 * at that as well). But with PKCS#12 we don't have such high
+	 * requirements, and we can slack off on our validation.
+	 */
+
+	asn = egg_asn1x_create_and_decode_full (pkix_asn1_tab, "pkcs-12-PFX",
+	                                        data, EGG_ASN1X_NO_STRICT);
 	if (!asn)
 		goto done;
 
@@ -1569,28 +1515,24 @@ parse_der_pkcs12 (GcrParser *self,
 		goto done;
 	}
 
-	element = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "authSafe", "content", NULL));
-	if (!element)
-		goto done;
-
-	asn_content = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab, "pkcs-7-Data", element);
-	if (!asn_content)
+	content = egg_asn1x_get_any_as (egg_asn1x_node (asn, "authSafe", "content", NULL),
+	                                pkix_asn1_tab, "pkcs-7-Data");
+	if (!content)
 		goto done;
 
-	content = egg_asn1x_get_string_as_bytes (asn_content);
-	if (!content)
+	string = egg_asn1x_get_string_as_bytes (content);
+	if (!string)
 		goto done;
 
-	ret = verify_pkcs12_safe (self, asn, content);
+	ret = verify_pkcs12_safe (self, asn, string);
 	if (ret == SUCCESS)
-		ret = handle_pkcs12_safe (self, content);
+		ret = handle_pkcs12_safe (self, string);
 
 done:
-	if (element)
-		g_bytes_unref (element);
 	if (content)
-		g_bytes_unref (content);
-	egg_asn1x_destroy (asn_content);
+		egg_asn1x_destroy (content);
+	if (string)
+		g_bytes_unref (string);
 	egg_asn1x_destroy (asn);
 	pop_parsed (self, parsed);
 	return ret;
diff --git a/gcr/gcr-subject-public-key.c b/gcr/gcr-subject-public-key.c
index f41b127..46c8d42 100644
--- a/gcr/gcr-subject-public-key.c
+++ b/gcr/gcr-subject-public-key.c
@@ -528,7 +528,6 @@ rsa_subject_public_key_from_attributes (GckAttributes *attrs,
 	GNode *key_asn;
 	GNode *params_asn;
 	GBytes *key;
-	GBytes *params;
 	GBytes *usg;
 
 	_gcr_oids_init ();
@@ -562,17 +561,14 @@ rsa_subject_public_key_from_attributes (GckAttributes *attrs,
 
 	egg_asn1x_set_null (params_asn);
 
-	params = egg_asn1x_encode (params_asn, g_realloc);
-	egg_asn1x_destroy (params_asn);
-
 	egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
 	                           key, g_bytes_get_size (key) * 8);
 
 	egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), GCR_OID_PKIX1_RSA);
-	egg_asn1x_set_element_raw (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params);
+	egg_asn1x_set_any_from (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params_asn);
 
+	egg_asn1x_destroy (params_asn);
 	g_bytes_unref (key);
-	g_bytes_unref (params);
 	return TRUE;
 }
 
@@ -627,7 +623,6 @@ dsa_subject_public_key_from_attributes (GckAttributes *attrs,
 	const GckAttribute *value, *g, *q, *p;
 	GNode *key_asn, *params_asn;
 	GBytes *key;
-	GBytes *params;
 
 	_gcr_oids_init ();
 
@@ -680,17 +675,14 @@ dsa_subject_public_key_from_attributes (GckAttributes *attrs,
 	key = egg_asn1x_encode (key_asn, NULL);
 	egg_asn1x_destroy (key_asn);
 
-	params = egg_asn1x_encode (params_asn, NULL);
-	egg_asn1x_destroy (params_asn);
-
 	egg_asn1x_set_bits_as_raw (egg_asn1x_node (info_asn, "subjectPublicKey", NULL),
 	                           key, g_bytes_get_size (key) * 8);
-	egg_asn1x_set_element_raw (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params);
+	egg_asn1x_set_any_from (egg_asn1x_node (info_asn, "algorithm", "parameters", NULL), params_asn);
 
 	egg_asn1x_set_oid_as_quark (egg_asn1x_node (info_asn, "algorithm", "algorithm", NULL), GCR_OID_PKIX1_DSA);
 
 	g_bytes_unref (key);
-	g_bytes_unref (params);
+	egg_asn1x_destroy (params_asn);
 	return TRUE;
 }
 
@@ -785,7 +777,7 @@ calculate_rsa_key_size (GBytes *data)
 	asn = egg_asn1x_create_and_decode (pk_asn1_tab, "RSAPublicKey", data);
 	g_return_val_if_fail (asn, 0);
 
-	content = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "modulus", NULL));
+	content = egg_asn1x_get_value_raw (egg_asn1x_node (asn, "modulus", NULL));
 	if (!content)
 		g_return_val_if_reached (0);
 
@@ -799,16 +791,16 @@ calculate_rsa_key_size (GBytes *data)
 }
 
 static guint
-calculate_dsa_params_size (GBytes *data)
+calculate_dsa_params_size (GNode *params)
 {
 	GNode *asn;
 	GBytes *content;
 	guint key_size;
 
-	asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", data);
+	asn = egg_asn1x_get_any_as (params, pk_asn1_tab, "DSAParameters");
 	g_return_val_if_fail (asn, 0);
 
-	content = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "p", NULL));
+	content = egg_asn1x_get_value_raw (egg_asn1x_node (asn, "p", NULL));
 	if (!content)
 		g_return_val_if_reached (0);
 
@@ -825,6 +817,7 @@ guint
 _gcr_subject_public_key_calculate_size (GNode *subject_public_key)
 {
 	GBytes *key;
+	GNode *params;
 	guint key_size = 0;
 	guint n_bits;
 	GQuark oid;
@@ -843,9 +836,8 @@ _gcr_subject_public_key_calculate_size (GNode *subject_public_key)
 
 	/* The DSA key size is discovered by the prime in params */
 	} else if (oid == GCR_OID_PKIX1_DSA) {
-		key = egg_asn1x_get_element_raw (egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL));
-		key_size = calculate_dsa_params_size (key);
-		g_bytes_unref (key);
+		params = egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL);
+		key_size = calculate_dsa_params_size (params);
 
 	} else {
 		g_message ("unsupported key algorithm: %s", g_quark_to_string (oid));
diff --git a/gcr/tests/files/usr0052-firefox.p12 b/gcr/tests/files/usr0052-firefox.p12
new file mode 100644
index 0000000..eff8c1e
Binary files /dev/null and b/gcr/tests/files/usr0052-firefox.p12 differ
diff --git a/gcr/tests/test-parser.c b/gcr/tests/test-parser.c
index b022fe3..18164a0 100644
--- a/gcr/tests/test-parser.c
+++ b/gcr/tests/test-parser.c
@@ -124,6 +124,9 @@ authenticate (GcrParser *par, gint state, gpointer user_data)
 	case 0:
 		gcr_parser_add_password (test->parser, "booo");
 		return TRUE;
+	case 1:
+		gcr_parser_add_password (test->parser, "usr0052");
+		return TRUE;
 	default:
 		g_printerr ("decryption didn't work for: %s", test->filedesc);
 		g_assert_not_reached ();



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