From: Stef Walter Date: Wed, 20 Jun 2012 06:15:20 +0000 (+0200) Subject: egg: Rework how DER parsing works X-Git-Tag: upstream/3.7.5~111 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ae6f001f386df172f966e055516c0b968379804a;p=platform%2Fupstream%2Fgcr.git 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 --- 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 #if 0 +#include 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 ();