svn rev #25613: trunk/src/lib/krb5/asn.1/

ghudson@MIT.EDU ghudson at MIT.EDU
Fri Jan 6 16:06:10 EST 2012


http://src.mit.edu/fisheye/changelog/krb5/?cs=25613
Commit By: ghudson
Log Message:
Support ASN.1 encoding without the outer tag

In order to support implicit tagging, make it possible to ASN.1-encode
a value without its outer tag, instead remembering the construction
bit of the omitted tag.

A cleaner design would be to have separate functions for encoding a
value's contents and its tag.  However, we can't do that for atype_fn
or atype_opaque, and the possible indirections between types and
fields mean we want to stay at the "encode everything" level for as
long as possible to allow implicit tagging of the largest possible
subset of types.  If we can get rid of atype_fn, we may be able to
switch to the cleaner design with some adjustments to atype_opaque.


Changed Files:
U   trunk/src/lib/krb5/asn.1/asn1_encode.c
Modified: trunk/src/lib/krb5/asn.1/asn1_encode.c
===================================================================
--- trunk/src/lib/krb5/asn.1/asn1_encode.c	2012-01-06 20:52:12 UTC (rev 25612)
+++ trunk/src/lib/krb5/asn.1/asn1_encode.c	2012-01-06 21:06:10 UTC (rev 25613)
@@ -237,15 +237,28 @@
 static asn1_error_code
 encode_a_field(asn1buf *buf, const void *val,
                const struct field_info *field,
-               unsigned int *retlen);
+               unsigned int *retlen, asn1_construction *omit_tag);
 
-asn1_error_code
-krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val,
-                            const struct atype_info *a, unsigned int *retlen)
+/* Encode a value according to a type.  If omit_tag is non-NULL, omit the
+ * outer tag and return its construction bit instead. */
+static asn1_error_code
+encode_type(asn1buf *buf, const void *val, const struct atype_info *a,
+            unsigned int *retlen, asn1_construction *omit_tag)
 {
     asn1_error_code retval;
+    asn1_class tagclass = UNIVERSAL;
+    asn1_construction construction = PRIMITIVE;
+    asn1_tagnum tagnum = -1;
     unsigned int length, sum = 0;
 
+    /*
+     * In the switch statement, do one of the following: (1) encode the
+     * contents of val and set tagclass, construction, and tagnum to the
+     * appropriate values for the tag; (2) encode the contents and tag, leaving
+     * tagnum alone (won't work with implicit tagging); (3) delegate the whole
+     * process to a subcall.  If not returning immediately, set length to the
+     * number of bytes encoded.
+     */
     switch (a->type) {
     case atype_primitive:
     case atype_fn:
@@ -254,61 +267,60 @@
         assert(prim->enc != NULL);
         retval = prim->enc(buf, val, &length);
         if (retval) return retval;
-        sum += length;
-        if (a->type == atype_primitive) {
-            retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, prim->tagval,
-                                   sum, &length);
-            if (retval) return retval;
-            sum += length;
-        }
-        *retlen = sum;
-        return 0;
+        if (a->type == atype_primitive)
+            tagnum = prim->tagval;
+        break;
     }
     case atype_sequence:
         assert(a->tinfo != NULL);
-        return just_encode_sequence(buf, val, a->tinfo, retlen);
+        retval = just_encode_sequence(buf, val, a->tinfo, &length);
+        if (retval)
+            return retval;
+        construction = CONSTRUCTED;
+        tagnum = ASN1_SEQUENCE;
+        break;
     case atype_ptr:
     {
         const struct ptr_info *ptr = a->tinfo;
         assert(ptr->basetype != NULL);
-        return krb5int_asn1_encode_a_thing(buf, LOADPTR(val, ptr),
-                                           ptr->basetype, retlen);
+        return encode_type(buf, LOADPTR(val, ptr), ptr->basetype, retlen,
+                           omit_tag);
     }
     case atype_field:
         assert(a->tinfo != NULL);
-        return encode_a_field(buf, val, a->tinfo, retlen);
+        return encode_a_field(buf, val, a->tinfo, retlen, omit_tag);
     case atype_nullterm_sequence_of:
     case atype_nonempty_nullterm_sequence_of:
         assert(a->tinfo != NULL);
-        return encode_nullterm_sequence_of(buf, val, a->tinfo,
-                                           a->type == atype_nullterm_sequence_of,
-                                           retlen);
+        retval = encode_nullterm_sequence_of(buf, val, a->tinfo,
+                                             a->type ==
+                                             atype_nullterm_sequence_of,
+                                             &length);
+        if (retval)
+            return retval;
+        construction = CONSTRUCTED;
+        tagnum = ASN1_SEQUENCE;
+        break;
     case atype_tagged_thing:
     {
         const struct tagged_info *tag = a->tinfo;
-        retval = krb5int_asn1_encode_a_thing(buf, val, tag->basetype, &length);
-        if (retval) return retval;
-        sum = length;
-        retval = asn1_make_tag(buf, tag->tagtype, tag->construction,
-                               tag->tagval, sum, &length);
-        if (retval) return retval;
-        sum += length;
-        *retlen = sum;
-        return 0;
+        retval = encode_type(buf, val, tag->basetype, &length, NULL);
+        if (retval)
+            return retval;
+        tagclass = tag->tagtype;
+        construction = tag->construction;
+        tagnum = tag->tagval;
+        break;
     }
     case atype_int:
     {
         const struct int_info *tinfo = a->tinfo;
         assert(tinfo->loadint != NULL);
         retval = asn1_encode_integer(buf, tinfo->loadint(val), &length);
-        if (retval) return retval;
-        sum = length;
-        retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_INTEGER, sum,
-                               &length);
-        if (retval) return retval;
-        sum += length;
-        *retlen = sum;
-        return 0;
+        if (retval)
+            return retval;
+        tagnum = ASN1_INTEGER;
+        break;
     }
     case atype_uint:
     {
@@ -316,14 +328,10 @@
         assert(tinfo->loaduint != NULL);
         retval = asn1_encode_unsigned_integer(buf, tinfo->loaduint(val),
                                               &length);
-        if (retval) return retval;
-        sum = length;
-        retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_INTEGER, sum,
-                               &length);
-        if (retval) return retval;
-        sum += length;
-        *retlen = sum;
-        return 0;
+        if (retval)
+            return retval;
+        tagnum = ASN1_INTEGER;
+        break;
     }
     case atype_min:
     case atype_max:
@@ -336,38 +344,65 @@
         assert(a->type != atype_opaque);
         abort();
     }
+
+    sum += length;
+    assert(omit_tag == NULL || tagnum != -1);
+    if (omit_tag == NULL && tagnum >= 0) {
+        /* We have not yet encoded the outer tag and should do so. */
+        retval = asn1_make_tag(buf, tagclass, construction, tagnum, sum,
+                               &length);
+        if (retval)
+            return retval;
+        sum += length;
+    } else if (omit_tag != NULL) {
+        /* Don't encode the tag; report its construction bit to the caller. */
+        *omit_tag = construction;
+    }
+
+    *retlen = sum;
+    return 0;
 }
 
+/*
+ * Encode a value according to a field specification, adding a context tag if
+ * specified.  If omit_tag is non-NULL, omit the outer tag and return its
+ * construction bit instead (only valid if the field has no context tag).
+ */
 static asn1_error_code
-encode_a_field(asn1buf *buf, const void *val,
-               const struct field_info *field,
-               unsigned int *retlen)
+encode_a_field(asn1buf *buf, const void *val, const struct field_info *field,
+               unsigned int *retlen, asn1_construction *omit_tag)
 {
     asn1_error_code retval;
-    unsigned int sum = 0;
+    asn1_class tagclass = UNIVERSAL;
+    asn1_construction construction = PRIMITIVE;
+    asn1_tagnum tagnum = -1;
+    unsigned int sum = 0, length;
 
     if (val == NULL) return ASN1_MISSING_FIELD;
+    assert(omit_tag == NULL || field->tag < 0);
 
+    /*
+     * In the switch statement, either (1) encode the contents of the field and
+     * set tagclass, construction, and tagnum to the appropriate values for the
+     * tag; (2) encode the contents and tag, leaving tagnum alone; (3) delegate
+     * the whole process to a subcall (only an option if the field has no
+     * context tag).  If not returning immediately, set length to the number of
+     * bytes encoded.
+     */
     switch (field->ftype) {
     case field_immediate:
     {
-        unsigned int length;
-
         retval = asn1_encode_integer(buf, (asn1_intmax) field->dataoff,
                                      &length);
-        if (retval) return retval;
-        sum += length;
-        retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_INTEGER, sum,
-                               &length);
-        if (retval) return retval;
-        sum += length;
+        if (retval)
+            return retval;
+        tagnum = ASN1_INTEGER;
         break;
     }
     case field_sequenceof_len:
     {
         const void *dataptr, *lenptr;
         int slen;
-        unsigned int length;
         const struct atype_info *a;
         const struct ptr_info *ptrinfo;
 
@@ -408,21 +443,20 @@
         if (slen != 0 && dataptr == NULL)
             return ASN1_MISSING_FIELD;
         retval = encode_sequence_of(buf, slen, dataptr, a, &length);
-        if (retval) return retval;
-        sum += length;
+        if (retval)
+            return retval;
+        construction = CONSTRUCTED;
+        tagnum = ASN1_SEQUENCE;
         break;
     }
     case field_normal:
     {
-        const void *dataptr;
-        unsigned int length;
-
-        dataptr = (const char *)val + field->dataoff;
-        retval = krb5int_asn1_encode_a_thing(buf, dataptr, field->atype,
-                                             &length);
+        const void *dataptr = (const char *)val + field->dataoff;
+        if (omit_tag != NULL)
+            return encode_type(buf, dataptr, field->atype, retlen, omit_tag);
+        retval = encode_type(buf, dataptr, field->atype, &length, NULL);
         if (retval)
             return retval;
-        sum += length;
         break;
     }
     case field_string:
@@ -430,7 +464,6 @@
         const void *dataptr, *lenptr;
         const struct atype_info *a;
         size_t slen;
-        unsigned int length;
         const struct string_info *string;
 
         dataptr = (const char *)val + field->dataoff;
@@ -472,14 +505,10 @@
         retval = string->enclen(buf, (unsigned int) slen, dataptr, &length);
         if (retval)
             return retval;
-        sum += length;
-        if (a->type == atype_string) {
-            retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, string->tagval,
-                                   sum, &length);
-            if (retval)
-                return retval;
-            sum += length;
-        }
+        if (a->type == atype_string)
+            tagnum = string->tagval;
+        else
+            assert(omit_tag == NULL);
         break;
     }
     default:
@@ -488,8 +517,21 @@
         assert(__LINE__ == 0);
         abort();
     }
+
+    sum += length;
+    if (omit_tag == NULL && tagnum >= 0) {
+        /* We have not yet encoded the field's outer tag and should do so. */
+        retval = asn1_make_tag(buf, tagclass, construction, tagnum, sum,
+                               &length);
+        if (retval)
+            return retval;
+        sum += length;
+    } else if (omit_tag != NULL) {
+        /* Don't encode the tag; report its construction bit to the caller. */
+        *omit_tag = construction;
+    }
+
     if (field->tag >= 0) {
-        unsigned int length;
         retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, field->tag, sum,
                                 &length);
         if (retval) {
@@ -522,7 +564,7 @@
         else
             present = 0;
         if (present) {
-            retval = encode_a_field(buf, val, f, &length);
+            retval = encode_a_field(buf, val, f, &length, NULL);
             if (retval) return retval;
             sum += length;
         }
@@ -536,34 +578,12 @@
                      const struct seq_info *seq,
                      unsigned int *retlen)
 {
-    const struct field_info *fields = seq->fields;
-    size_t nfields = seq->n_fields;
     unsigned int optional;
-    asn1_error_code retval;
-    unsigned int sum = 0;
 
-    if (seq->optional)
-        optional = seq->optional(val);
-    else
-        /*
-         * In this case, none of the field descriptors should indicate
-         * that we examine any bits of this value.
-         */
-        optional = 0;
-    {
-        unsigned int length;
-        retval = encode_fields(buf, val, fields, nfields, optional, &length);
-        if (retval) return retval;
-        sum += length;
-    }
-    {
-        unsigned int length;
-        retval = asn1_make_sequence(buf, sum, &length);
-        if (retval) return retval;
-        sum += length;
-    }
-    *retlen = sum;
-    return 0;
+    /* If any fields might be optional, get a bitmask of optional fields. */
+    optional = (seq->optional == NULL) ? 0 : seq->optional(val);
+    return encode_fields(buf, val, seq->fields, seq->n_fields, optional,
+                         retlen);
 }
 
 static asn1_error_code
@@ -582,20 +602,22 @@
 
         assert(eltinfo->size != 0);
         eltptr = (const char *)val + i * eltinfo->size;
-        retval = krb5int_asn1_encode_a_thing(buf, eltptr, a, &length);
-        if (retval) return retval;
+        retval = encode_type(buf, eltptr, a, &length, NULL);
+        if (retval)
+            return retval;
         sum += length;
     }
-    {
-        unsigned int length;
-        retval = asn1_make_sequence(buf, sum, &length);
-        if (retval) return retval;
-        sum += length;
-    }
     *retlen = sum;
     return 0;
 }
 
+asn1_error_code
+krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val,
+                            const struct atype_info *a, unsigned int *retlen)
+{
+    return encode_type(buf, val, a, retlen, NULL);
+}
+
 krb5_error_code
 krb5int_asn1_do_full_encode(const void *rep, krb5_data **code,
                             const struct atype_info *a)
@@ -614,7 +636,7 @@
     if (retval)
         return retval;
 
-    retval = krb5int_asn1_encode_a_thing(buf, rep, a, &length);
+    retval = encode_type(buf, rep, a, &length, NULL);
     if (retval)
         goto cleanup;
     retval = asn12krb5_buf(buf, &d);



More information about the cvs-krb5 mailing list