From: Greg Hudson Date: Fri, 6 Jan 2012 21:06:10 +0000 (+0000) Subject: Support ASN.1 encoding without the outer tag X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=158156c569e9adee58564d12b436197b4c1307bf;p=krb5.git 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. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25613 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/krb5/asn.1/asn1_encode.c b/src/lib/krb5/asn.1/asn1_encode.c index f222423e9..3d82ac84f 100644 --- a/src/lib/krb5/asn.1/asn1_encode.c +++ b/src/lib/krb5/asn.1/asn1_encode.c @@ -237,15 +237,28 @@ just_encode_sequence(asn1buf *buf, const void *val, 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 @@ krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val, 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 @@ krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val, 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 @@ krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val, 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 @@ encode_a_field(asn1buf *buf, const void *val, 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 @@ encode_a_field(asn1buf *buf, const void *val, 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 @@ encode_a_field(asn1buf *buf, const void *val, 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 @@ encode_a_field(asn1buf *buf, const void *val, 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 @@ encode_fields(asn1buf *buf, const void *val, 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 @@ just_encode_sequence(asn1buf *buf, const void *val, 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 @@ encode_sequence_of(asn1buf *buf, int seqlen, const void *val, 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; - sum += length; - } - { - unsigned int length; - retval = asn1_make_sequence(buf, sum, &length); - if (retval) return retval; + retval = encode_type(buf, eltptr, a, &length, NULL); + 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 @@ krb5int_asn1_do_full_encode(const void *rep, krb5_data **code, 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);