Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  secasn1d.c   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/*
 * Support for DEcoding ASN.1 data based on BER/DER (Basic/Distinguished
 * Encoding Rules).
 */


/* #define DEBUG_ASN1D_STATES 1 */

#ifdef DEBUG_ASN1D_STATES
#include <stdio.h>
#define PR_Assert sec_asn1d_Assert
#endif

#include <limits.h>

#include "secasn1.h"
#include "secerr.h"

typedef enum {
    beforeIdentifier,
    duringIdentifier,
    afterIdentifier,
    beforeLength,
    duringLength,
    afterLength,
    beforeBitString,
    duringBitString,
    duringConstructedString,
    duringGroup,
    duringLeaf,
    duringSaveEncoding,
    duringSequence,
    afterConstructedString,
    afterGroup,
    afterExplicit,
    afterImplicit,
    afterInline,
    afterPointer,
    afterSaveEncoding,
    beforeEndOfContents,
    duringEndOfContents,
    afterEndOfContents,
    beforeChoice,
    duringChoice,
    afterChoice,
    notInUse
} sec_asn1d_parse_place;

#ifdef DEBUG_ASN1D_STATES
static const char *const place_names[] = {
    "beforeIdentifier",
    "duringIdentifier",
    "afterIdentifier",
    "beforeLength",
    "duringLength",
    "afterLength",
    "beforeBitString",
    "duringBitString",
    "duringConstructedString",
    "duringGroup",
    "duringLeaf",
    "duringSaveEncoding",
    "duringSequence",
    "afterConstructedString",
    "afterGroup",
    "afterExplicit",
    "afterImplicit",
    "afterInline",
    "afterPointer",
    "afterSaveEncoding",
    "beforeEndOfContents",
    "duringEndOfContents",
    "afterEndOfContents",
    "beforeChoice",
    "duringChoice",
    "afterChoice",
    "notInUse"
};

static const char *const class_names[] = {
    "UNIVERSAL",
    "APPLICATION",
    "CONTEXT_SPECIFIC",
    "PRIVATE"
};

static const char *const method_names[] = { "PRIMITIVE""CONSTRUCTED" };

static const char *const type_names[] = {
    "END_OF_CONTENTS",
    "BOOLEAN",
    "INTEGER",
    "BIT_STRING",
    "OCTET_STRING",
    "NULL",
    "OBJECT_ID",
    "OBJECT_DESCRIPTOR",
    "(type 08)",
    "REAL",
    "ENUMERATED",
    "EMBEDDED",
    "UTF8_STRING",
    "(type 0d)",
    "(type 0e)",
    "(type 0f)",
    "SEQUENCE",
    "SET",
    "NUMERIC_STRING",
    "PRINTABLE_STRING",
    "T61_STRING",
    "VIDEOTEXT_STRING",
    "IA5_STRING",
    "UTC_TIME",
    "GENERALIZED_TIME",
    "GRAPHIC_STRING",
    "VISIBLE_STRING",
    "GENERAL_STRING",
    "UNIVERSAL_STRING",
    "(type 1d)",
    "BMP_STRING",
    "HIGH_TAG_VALUE"
};

static const char *const flag_names[] = {
    /* flags, right to left */
    "OPTIONAL",
    "EXPLICIT",
    "ANY",
    "INLINE",
    "POINTER",
    "GROUP",
    "DYNAMIC",
    "SKIP",
    "INNER",
    "SAVE",
    ""/* decoder ignores "MAY_STREAM", */
    "SKIP_REST",
    "CHOICE",
    "NO_STREAM",
    "DEBUG_BREAK",
    "unknown 08",
    "unknown 10",
    "unknown 20",
    "unknown 40",
    "unknown 80"
};

static int /* bool */
formatKind(unsigned long kind, char *buf, int space_in_buffer)
{
    int i;
    unsigned long k = kind & SEC_ASN1_TAGNUM_MASK;
    unsigned long notag = kind & (SEC_ASN1_CHOICE | SEC_ASN1_POINTER |
                                  SEC_ASN1_INLINE | SEC_ASN1_ANY | SEC_ASN1_SAVE);

    buf[0] = 0;
    if ((kind & SEC_ASN1_CLASS_MASK) != SEC_ASN1_UNIVERSAL) {
        space_in_buffer -= snprintf(buf, space_in_buffer, " %s", class_names[(kind & SEC_ASN1_CLASS_MASK) >> 6]);
        buf += strlen(buf);
    }
    if (kind & SEC_ASN1_METHOD_MASK) {
        space_in_buffer -= snprintf(buf, space_in_buffer, " %s", method_names[1]);
        buf += strlen(buf);
    }
    if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) {
        if (k || !notag) {
            space_in_buffer -= snprintf(buf, space_in_buffer, " %s", type_names[k]);
            if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) &&
                (kind & SEC_ASN1_GROUP)) {
                buf += strlen(buf);
                space_in_buffer -= snprintf(buf, space_in_buffer, "_OF");
            }
        }
    } else {
        space_in_buffer -= snprintf(buf, space_in_buffer, " [%lu]", k);
    }
    buf += strlen(buf);

    for (k = kind >> 8, i = 0; k; k >>= 1, ++i) {
        if (k & 1) {
            space_in_buffer -= snprintf(buf, space_in_buffer, " %s", flag_names[i]);
            buf += strlen(buf);
        }
    }
    return notag != 0;
}

#endif /* DEBUG_ASN1D_STATES */

typedef enum {
    allDone,
    decodeError,
    keepGoing,
    needBytes
} sec_asn1d_parse_status;

struct subitem {
    const void *data;
    unsigned long len; /* only used for substrings */
    struct subitem *next;
};

typedef struct sec_asn1d_state_struct {
    SEC_ASN1DecoderContext *top;
    const SEC_ASN1Template *theTemplate;
    void *dest;

    void *our_mark; /* free on completion */

    struct sec_asn1d_state_struct *parent; /* aka prev */
    struct sec_asn1d_state_struct *child;  /* aka next */

    sec_asn1d_parse_place place;

    /*
     * XXX explain the next fields as clearly as possible...
     */

    unsigned char found_tag_modifiers;
    unsigned char expect_tag_modifiers;
    unsigned long check_tag_mask;
    unsigned long found_tag_number;
    unsigned long expect_tag_number;
    unsigned long underlying_kind;

    unsigned long contents_length;
    unsigned long pending;
    unsigned long consumed;

    int depth;

    /*
     * Bit strings have their length adjusted -- the first octet of the
     * contents contains a value between 0 and 7 which says how many bits
     * at the end of the octets are not actually part of the bit string;
     * when parsing bit strings we put that value here because we need it
     * later, for adjustment of the length (when the whole string is done).
     */

    unsigned int bit_string_unused_bits;

    /*
     * The following are used for indefinite-length constructed strings.
     */

    struct subitem *subitems_head;
    struct subitem *subitems_tail;

    PRPackedBool
        allocate,      /* when true, need to allocate the destination */
        endofcontents, /* this state ended up parsing its parent's end-of-contents octets */
        explicit,      /* we are handling an explicit header */
        indefinite,    /* the current item has indefinite-length encoding */
        missing,       /* an optional field that was not present */
        optional,      /* the template says this field may be omitted */
        substring;     /* this is a substring of a constructed string */

} sec_asn1d_state;

#define IS_HIGH_TAG_NUMBER(n) ((n) == SEC_ASN1_HIGH_TAG_NUMBER)
#define LAST_TAG_NUMBER_BYTE(b) (((b)&0x80) == 0)
#define TAG_NUMBER_BITS 7
#define TAG_NUMBER_MASK 0x7f

#define LENGTH_IS_SHORT_FORM(b) (((b)&0x80) == 0)
#define LONG_FORM_LENGTH(b) ((b)&0x7f)

#define HIGH_BITS(field, cnt) ((field) >> ((sizeof(field) * 8) - (cnt)))

/*
 * An "outsider" will have an opaque pointer to this, created by calling
 * SEC_ASN1DecoderStart().  It will be passed back in to all subsequent
 * calls to SEC_ASN1DecoderUpdate(), and when done it is passed to
 * SEC_ASN1DecoderFinish().
 */

struct sec_DecoderContext_struct {
    PLArenaPool *our_pool;     /* for our internal allocs */
    PLArenaPool *their_pool;   /* for destination structure allocs */
#ifdef SEC_ASN1D_FREE_ON_ERROR /*                                 \
                                * XXX see comment below (by same  \
                                * ifdef) that explains why this   \
                                * does not work (need more smarts \
                                * in order to free back to mark)  \
                                */

    /*
     * XXX how to make their_mark work in the case where they do NOT
     * give us a pool pointer?
     */

    void *their_mark; /* free on error */
#endif

    sec_asn1d_state *current;
    sec_asn1d_parse_status status;

    /* The maximum size the caller is willing to allow a single element
     * to be before returning an error.
     *
     * In the case of an indefinite length element, this is the sum total
     * of all child elements.
     *
     * In the case of a definite length element, this represents the maximum
     * size of the top-level element.
     */

    unsigned long max_element_size;

    SEC_ASN1NotifyProc notify_proc; /* call before/after handling field */
    void *notify_arg;               /* argument to notify_proc */
    PRBool during_notify;           /* true during call to notify_proc */

    SEC_ASN1WriteProc filter_proc; /* pass field bytes to this  */
    void *filter_arg;              /* argument to that function */
    PRBool filter_only;            /* do not allocate/store fields */
};

/*
 * XXX this is a fairly generic function that may belong elsewhere
 */

static void *
sec_asn1d_alloc(PLArenaPool *poolp, unsigned long len)
{
    void *thing;

    if (poolp != NULL) {
        /*
         * Allocate from the pool.
         */

        thing = PORT_ArenaAlloc(poolp, len);
    } else {
        /*
         * Allocate generically.
         */

        thing = PORT_Alloc(len);
    }

    return thing;
}

/*
 * XXX this is a fairly generic function that may belong elsewhere
 */

static void *
sec_asn1d_zalloc(PLArenaPool *poolp, unsigned long len)
{
    void *thing;

    thing = sec_asn1d_alloc(poolp, len);
    if (thing != NULL)
        PORT_Memset(thing, 0, len);
    return thing;
}

static sec_asn1d_state *
sec_asn1d_push_state(SEC_ASN1DecoderContext *cx,
                     const SEC_ASN1Template *theTemplate,
                     void *dest, PRBool new_depth)
{
    sec_asn1d_state *state, *new_state;

    state = cx->current;

    PORT_Assert(state == NULL || state->child == NULL);

    if (state != NULL) {
        PORT_Assert(state->our_mark == NULL);
        state->our_mark = PORT_ArenaMark(cx->our_pool);
    }

    if (theTemplate == NULL) {
        PORT_SetError(SEC_ERROR_BAD_TEMPLATE);
        goto loser;
    }

    new_state = (sec_asn1d_state *)sec_asn1d_zalloc(cx->our_pool,
                                                    sizeof(*new_state));
    if (new_state == NULL) {
        goto loser;
    }

    new_state->top = cx;
    new_state->parent = state;
    new_state->theTemplate = theTemplate;
    new_state->place = notInUse;
    if (dest != NULL)
        new_state->dest = (char *)dest + theTemplate->offset;

    if (state != NULL) {
        new_state->depth = state->depth;
        if (new_depth) {
            if (++new_state->depth > SEC_ASN1D_MAX_DEPTH) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                goto loser;
            }
        }
        state->child = new_state;
    }

    cx->current = new_state;
    return new_state;

loser:
    cx->status = decodeError;
    if (state != NULL) {
        PORT_ArenaRelease(cx->our_pool, state->our_mark);
        state->our_mark = NULL;
    }
    return NULL;
}

static void
sec_asn1d_scrub_state(sec_asn1d_state *state)
{
    /*
     * Some default "scrubbing".
     * XXX right set of initializations?
     */

    state->place = beforeIdentifier;
    state->endofcontents = PR_FALSE;
    state->indefinite = PR_FALSE;
    state->missing = PR_FALSE;
    PORT_Assert(state->consumed == 0);
}

static void
sec_asn1d_notify_before(SEC_ASN1DecoderContext *cx, void *dest, int depth)
{
    if (cx->notify_proc == NULL)
        return;

    cx->during_notify = PR_TRUE;
    (*cx->notify_proc)(cx->notify_arg, PR_TRUE, dest, depth);
    cx->during_notify = PR_FALSE;
}

static void
sec_asn1d_notify_after(SEC_ASN1DecoderContext *cx, void *dest, int depth)
{
    if (cx->notify_proc == NULL)
        return;

    cx->during_notify = PR_TRUE;
    (*cx->notify_proc)(cx->notify_arg, PR_FALSE, dest, depth);
    cx->during_notify = PR_FALSE;
}

static sec_asn1d_state *
sec_asn1d_init_state_based_on_template(sec_asn1d_state *state)
{
    PRBool explicit, optional, universal;
    unsigned char expect_tag_modifiers;
    unsigned long encode_kind, under_kind;
    unsigned long check_tag_mask, expect_tag_number;

    /* XXX Check that both of these tests are really needed/appropriate. */
    if (state == NULL || state->top->status == decodeError)
        return state;

    encode_kind = state->theTemplate->kind;

    if (encode_kind & SEC_ASN1_SAVE) {
        /*
         * This is a "magic" field that saves away all bytes, allowing
         * the immediately following field to still be decoded from this
         * same spot -- sort of a fork.
         */

        /* check that there are no extraneous bits */
        PORT_Assert(encode_kind == SEC_ASN1_SAVE);
        if (state->top->filter_only) {
            /*
             * If we are not storing, then we do not do the SAVE field
             * at all.  Just move ahead to the "real" field instead,
             * doing the appropriate notify calls before and after.
             */

            sec_asn1d_notify_after(state->top, state->dest, state->depth);
            /*
             * Since we are not storing, allow for our current dest value
             * to be NULL.  (This might not actually occur, but right now I
             * cannot convince myself one way or the other.)  If it is NULL,
             * assume that our parent dest can help us out.
             */

            if (state->dest == NULL)
                state->dest = state->parent->dest;
            else
                state->dest = (char *)state->dest - state->theTemplate->offset;
            state->theTemplate++;
            if (state->dest != NULL)
                state->dest = (char *)state->dest + state->theTemplate->offset;
            sec_asn1d_notify_before(state->top, state->dest, state->depth);
            encode_kind = state->theTemplate->kind;
            PORT_Assert((encode_kind & SEC_ASN1_SAVE) == 0);
        } else {
            sec_asn1d_scrub_state(state);
            state->place = duringSaveEncoding;
            state = sec_asn1d_push_state(state->top, SEC_AnyTemplate,
                                         state->dest, PR_FALSE);
            if (state != NULL)
                state = sec_asn1d_init_state_based_on_template(state);
            return state;
        }
    }

    universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
                    ? PR_TRUE
                    : PR_FALSE;

    explicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~SEC_ASN1_EXPLICIT;

    optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~SEC_ASN1_OPTIONAL;

    PORT_Assert(!(explicit && universal)); /* bad templates */

    encode_kind &= ~SEC_ASN1_DYNAMIC;
    encode_kind &= ~SEC_ASN1_MAY_STREAM;

    if (encode_kind & SEC_ASN1_CHOICE) {
#if 0 /* XXX remove? */
      sec_asn1d_state *child = sec_asn1d_push_state(state->top, state->theTemplate, state->dest, PR_FALSE);
      if ((sec_asn1d_state *)NULL == child) {
        return (sec_asn1d_state *)NULL;
      }

      child->allocate = state->allocate;
      child->place = beforeChoice;
      return child;
#else
        state->place = beforeChoice;
        return state;
#endif
    }

    if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || (!universal && !explicit)) {
        const SEC_ASN1Template *subt;
        void *dest;
        PRBool child_allocate;

        PORT_Assert((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0);

        sec_asn1d_scrub_state(state);
        child_allocate = PR_FALSE;

        if (encode_kind & SEC_ASN1_POINTER) {
            /*
             * A POINTER means we need to allocate the destination for
             * this field.  But, since it may also be an optional field,
             * we defer the allocation until later; we just record that
             * it needs to be done.
             *
             * There are two possible scenarios here -- one is just a
             * plain POINTER (kind of like INLINE, except with allocation)
             * and the other is an implicitly-tagged POINTER.  We don't
             * need to do anything special here for the two cases, but
             * since the template definition can be tricky, we do check
             * that there are no extraneous bits set in encode_kind.
             *
             * XXX The same conditions which assert should set an error.
             */

            if (universal) {
                /*
                 * "universal" means this entry is a standalone POINTER;
                 * there should be no other bits set in encode_kind.
                 */

                PORT_Assert(encode_kind == SEC_ASN1_POINTER);
            } else {
                /*
                 * If we get here we have an implicitly-tagged field
                 * that needs to be put into a POINTER.  The subtemplate
                 * will determine how to decode the field, but encode_kind
                 * describes the (implicit) tag we are looking for.
                 * The non-tag bits of encode_kind will be ignored by
                 * the code below; none of them should be set, however,
                 * except for the POINTER bit itself -- so check that.
                 */

                PORT_Assert((encode_kind & ~SEC_ASN1_TAG_MASK) == SEC_ASN1_POINTER);
            }
            if (!state->top->filter_only)
                child_allocate = PR_TRUE;
            dest = NULL;
            state->place = afterPointer;
        } else {
            dest = state->dest;
            if (encode_kind & SEC_ASN1_INLINE) {
                /* check that there are no extraneous bits */
                PORT_Assert(encode_kind == SEC_ASN1_INLINE && !optional);
                state->place = afterInline;
            } else {
                state->place = afterImplicit;
            }
        }

        state->optional = optional;
        subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest, PR_FALSE);
        state = sec_asn1d_push_state(state->top, subt, dest, PR_FALSE);
        if (state == NULL)
            return NULL;

        state->allocate = child_allocate;

        if (universal) {
            state = sec_asn1d_init_state_based_on_template(state);
            if (state != NULL) {
                /*
                 * If this field is optional, we need to record that on
                 * the pushed child so it won't fail if the field isn't
                 * found.  I can't think of a way that this new state
                 * could already have optional set (which we would wipe
                 * out below if our local optional is not set) -- but
                 * just to be sure, assert that it isn't set.
                 */

                PORT_Assert(!state->optional);
                state->optional = optional;
            }
            return state;
        }

        under_kind = state->theTemplate->kind;
        under_kind &= ~SEC_ASN1_MAY_STREAM;
    } else if (explicit) {
        /*
         * For explicit, we only need to match the encoding tag next,
         * then we will push another state to handle the entire inner
         * part.  In this case, there is no underlying kind which plays
         * any part in the determination of the outer, explicit tag.
         * So we just set under_kind to 0, which is not a valid tag,
         * and the rest of the tag matching stuff should be okay.
         */

        under_kind = 0;
    } else {
        /*
         * Nothing special; the underlying kind and the given encoding
         * information are the same.
         */

        under_kind = encode_kind;
    }

    /* XXX is this the right set of bits to test here? */
    PORT_Assert((under_kind & (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)) == 0);

    if (encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) {
        PORT_Assert(encode_kind == under_kind);
        if (encode_kind & SEC_ASN1_SKIP) {
            PORT_Assert(!optional);
            PORT_Assert(encode_kind == SEC_ASN1_SKIP);
            state->dest = NULL;
        }
        check_tag_mask = 0;
        expect_tag_modifiers = 0;
        expect_tag_number = 0;
    } else {
        check_tag_mask = SEC_ASN1_TAG_MASK;
        expect_tag_modifiers = (unsigned char)encode_kind & SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK;
        /*
         * XXX This assumes only single-octet identifiers.  To handle
         * the HIGH TAG form we would need to do some more work, especially
         * in how to specify them in the template, because right now we
         * do not provide a way to specify more *tag* bits in encode_kind.
         */

        expect_tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;

        switch (under_kind & SEC_ASN1_TAGNUM_MASK) {
            case SEC_ASN1_SET:
                /*
                 * XXX A plain old SET (as opposed to a SET OF) is not implemented.
                 * If it ever is, remove this assert...
                 */

                PORT_Assert((under_kind & SEC_ASN1_GROUP) != 0);
            /* fallthru */
            case SEC_ASN1_SEQUENCE:
                expect_tag_modifiers |= SEC_ASN1_CONSTRUCTED;
                break;
            case SEC_ASN1_BIT_STRING:
            case SEC_ASN1_BMP_STRING:
            case SEC_ASN1_GENERALIZED_TIME:
            case SEC_ASN1_IA5_STRING:
            case SEC_ASN1_OCTET_STRING:
            case SEC_ASN1_PRINTABLE_STRING:
            case SEC_ASN1_T61_STRING:
            case SEC_ASN1_UNIVERSAL_STRING:
            case SEC_ASN1_UTC_TIME:
            case SEC_ASN1_UTF8_STRING:
            case SEC_ASN1_VISIBLE_STRING:
                check_tag_mask &= ~SEC_ASN1_CONSTRUCTED;
                break;
        }
    }

    state->check_tag_mask = check_tag_mask;
    state->expect_tag_modifiers = expect_tag_modifiers;
    state->expect_tag_number = expect_tag_number;
    state->underlying_kind = under_kind;
    state->explicit = explicit;
    state->optional = optional;

    sec_asn1d_scrub_state(state);

    return state;
}

static sec_asn1d_state *
sec_asn1d_get_enclosing_construct(sec_asn1d_state *state)
{
    for (state = state->parent; state; state = state->parent) {
        sec_asn1d_parse_place place = state->place;
        if (place != afterImplicit &&
            place != afterPointer &&
            place != afterInline &&
            place != afterSaveEncoding &&
            place != duringSaveEncoding &&
            place != duringChoice) {

            /* we've walked up the stack to a state that represents
            ** the enclosing construct.
            */

            break;
        }
    }
    return state;
}

static PRBool
sec_asn1d_parent_allows_EOC(sec_asn1d_state *state)
{
    /* get state of enclosing construct. */
    state = sec_asn1d_get_enclosing_construct(state);
    if (state) {
        sec_asn1d_parse_place place = state->place;
        /* Is it one of the types that permits an unexpected EOC? */
        int eoc_permitted =
            (place == duringGroup ||
             place == duringConstructedString ||
             state->child->optional);
        return (state->indefinite && eoc_permitted) ? PR_TRUE : PR_FALSE;
    }
    return PR_FALSE;
}

static unsigned long
sec_asn1d_parse_identifier(sec_asn1d_state *state,
                           const char *buf, unsigned long len)
{
    unsigned char byte;
    unsigned char tag_number;

    PORT_Assert(state->place == beforeIdentifier);

    if (len == 0) {
        state->top->status = needBytes;
        return 0;
    }

    byte = (unsigned char)*buf;
#ifdef DEBUG_ASN1D_STATES
    {
        int bufsize = 256;
        char kindBuf[bufsize];
        formatKind(byte, kindBuf, bufsize);
        printf("Found tag %02x %s\n", byte, kindBuf);
    }
#endif
    tag_number = byte & SEC_ASN1_TAGNUM_MASK;

    if (IS_HIGH_TAG_NUMBER(tag_number)) {
        state->place = duringIdentifier;
        state->found_tag_number = 0;
        /*
         * Actually, we have no idea how many bytes are pending, but we
         * do know that it is at least 1.  That is all we know; we have
         * to look at each byte to know if there is another, etc.
         */

        state->pending = 1;
    } else {
        if (byte == 0 && sec_asn1d_parent_allows_EOC(state)) {
            /*
             * Our parent has indefinite-length encoding, and the
             * entire tag found is 0, so it seems that we have hit the
             * end-of-contents octets.  To handle this, we just change
             * our state to that which expects to get the bytes of the
             * end-of-contents octets and let that code re-read this byte
             * so that our categorization of field types is correct.
             * After that, our parent will then deal with everything else.
             */

            state->place = duringEndOfContents;
            state->pending = 2;
            state->found_tag_number = 0;
            state->found_tag_modifiers = 0;
            /*
             * We might be an optional field that is, as we now find out,
             * missing.  Give our parent a clue that this happened.
             */

            if (state->optional)
                state->missing = PR_TRUE;
            return 0;
        }
        state->place = afterIdentifier;
        state->found_tag_number = tag_number;
    }
    state->found_tag_modifiers = byte & ~SEC_ASN1_TAGNUM_MASK;

    return 1;
}

static unsigned long
sec_asn1d_parse_more_identifier(sec_asn1d_state *state,
                                const char *buf, unsigned long len)
{
    unsigned char byte;
    int count;

    PORT_Assert(state->pending == 1);
    PORT_Assert(state->place == duringIdentifier);

    if (len == 0) {
        state->top->status = needBytes;
        return 0;
    }

    count = 0;

    while (len && state->pending) {
        if (HIGH_BITS(state->found_tag_number, TAG_NUMBER_BITS) != 0) {
            /*
             * The given high tag number overflows our container;
             * just give up.  This is not likely to *ever* happen.
             */

            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return 0;
        }

        state->found_tag_number <<= TAG_NUMBER_BITS;

        byte = (unsigned char)buf[count++];
        state->found_tag_number |= (byte & TAG_NUMBER_MASK);

        len--;
        if (LAST_TAG_NUMBER_BYTE(byte))
            state->pending = 0;
    }

    if (state->pending == 0)
        state->place = afterIdentifier;

    return count;
}

static void
sec_asn1d_confirm_identifier(sec_asn1d_state *state)
{
    PRBool match;

    PORT_Assert(state->place == afterIdentifier);

    match = (PRBool)(((state->found_tag_modifiers & state->check_tag_mask) == state->expect_tag_modifiers) && ((state->found_tag_number & state->check_tag_mask) == state->expect_tag_number));
    if (match) {
        state->place = beforeLength;
    } else {
        if (state->optional) {
            state->missing = PR_TRUE;
            state->place = afterEndOfContents;
        } else {
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
        }
    }
}

static unsigned long
sec_asn1d_parse_length(sec_asn1d_state *state,
                       const char *buf, unsigned long len)
{
    unsigned char byte;

    PORT_Assert(state->place == beforeLength);

    if (len == 0) {
        state->top->status = needBytes;
        return 0;
    }

    /*
     * The default/likely outcome.  It may get adjusted below.
     */

    state->place = afterLength;

    byte = (unsigned char)*buf;

    if (LENGTH_IS_SHORT_FORM(byte)) {
        state->contents_length = byte;
    } else {
        state->contents_length = 0;
        state->pending = LONG_FORM_LENGTH(byte);
        if (state->pending == 0) {
            state->indefinite = PR_TRUE;
        } else {
            state->place = duringLength;
        }
    }

    /* If we're parsing an ANY, SKIP, or SAVE template, and
    ** the object being saved is definite length encoded and constructed,
    ** there's no point in decoding that construct's members.
    ** So, just forget it's constructed and treat it as primitive.
    ** (SAVE appears as an ANY at this point)
    */

    if (!state->indefinite &&
        (state->underlying_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP))) {
        state->found_tag_modifiers &= ~SEC_ASN1_CONSTRUCTED;
    }

    return 1;
}

static unsigned long
sec_asn1d_parse_more_length(sec_asn1d_state *state,
                            const char *buf, unsigned long len)
{
    int count;

    PORT_Assert(state->pending > 0);
    PORT_Assert(state->place == duringLength);

    if (len == 0) {
        state->top->status = needBytes;
        return 0;
    }

    count = 0;

    while (len && state->pending) {
        if (HIGH_BITS(state->contents_length, 9) != 0) {
            /*
             * The given full content length overflows our container;
             * just give up.
             */

            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return 0;
        }

        state->contents_length <<= 8;
        state->contents_length |= (unsigned char)buf[count++];

        len--;
        state->pending--;
    }

    if (state->pending == 0)
        state->place = afterLength;

    return count;
}

/*
 * Helper function for sec_asn1d_prepare_for_contents.
 * Checks that a value representing a number of bytes consumed can be
 * subtracted from a remaining length. If so, returns PR_TRUE.
 * Otherwise, sets the error SEC_ERROR_BAD_DER, indicates that there was a
 * decoding error in the given SEC_ASN1DecoderContext, and returns PR_FALSE.
 */

static PRBool
sec_asn1d_check_and_subtract_length(unsigned long *remaining,
                                    unsigned long consumed,
                                    SEC_ASN1DecoderContext *cx)
{
    PORT_Assert(remaining);
    PORT_Assert(cx);
    if (!remaining || !cx) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        cx->status = decodeError;
        return PR_FALSE;
    }
    if (*remaining < consumed) {
        PORT_SetError(SEC_ERROR_BAD_DER);
        cx->status = decodeError;
        return PR_FALSE;
    }
    *remaining -= consumed;
    return PR_TRUE;
}

static void
sec_asn1d_prepare_for_contents(sec_asn1d_state *state)
{
    SECItem *item;
    PLArenaPool *poolp;
    unsigned long alloc_len;
    sec_asn1d_state *parent;

#ifdef DEBUG_ASN1D_STATES
    {
        printf("Found Length %lu %s\n", state->contents_length,
               state->indefinite ? "indefinite" : "");
    }
#endif

    /**
     * The maximum length for a child element should be constrained to the
     * length remaining in the first definite length element in the ancestor
     * stack. If there is no definite length element in the ancestor stack,
     * there's nothing to constrain the length of the child, so there's no
     * further processing necessary.
     *
     * It's necessary to walk the ancestor stack, because it's possible to have
     * definite length children that are part of an indefinite length element,
     * which is itself part of an indefinite length element, and which is
     * ultimately part of a definite length element. A simple example of this
     * would be the handling of constructed OCTET STRINGs in BER encoding.
     *
     * This algorithm finds the first definite length element in the ancestor
     * stack, if any, and if so, ensures that the length of the child element
     * is consistent with the number of bytes remaining in the constraining
     * ancestor element (that is, after accounting for any other sibling
     * elements that may have been read).
     *
     * It's slightly complicated by the need to account both for integer
     * underflow and overflow, as well as ensure that for indefinite length
     * encodings, there's also enough space for the End-of-Contents (EOC)
     * octets (Tag = 0x00, Length = 0x00, or two bytes).
     */


    /* Determine the maximum length available for this element by finding the
     * first definite length ancestor, if any. */

    parent = sec_asn1d_get_enclosing_construct(state);
    while (parent && parent->indefinite) {
        parent = sec_asn1d_get_enclosing_construct(parent);
    }
    /* If parent is null, state is either the outermost state / at the top of
     * the stack, or the outermost state uses indefinite length encoding. In
     * these cases, there's nothing external to constrain this element, so
     * there's nothing to check. */

    if (parent) {
        unsigned long remaining = parent->pending;
        parent = state;
        do {
            if (!sec_asn1d_check_and_subtract_length(
                    &remaining, parent->consumed, state->top) ||
                /* If parent->indefinite is true, parent->contents_length is
                 * zero and this is a no-op. */

                !sec_asn1d_check_and_subtract_length(
                    &remaining, parent->contents_length, state->top) ||
                /* If parent->indefinite is true, then ensure there is enough
                 * space for an EOC tag of 2 bytes. */

                (parent->indefinite && !sec_asn1d_check_and_subtract_length(&remaining, 2, state->top))) {
                /* This element is larger than its enclosing element, which is
                 * invalid. */

                return;
            }
        } while ((parent = sec_asn1d_get_enclosing_construct(parent)) &&
                 parent->indefinite);
    }

    /*
     * XXX I cannot decide if this allocation should exclude the case
     *     where state->endofcontents is true -- figure it out!
     */

    if (state->allocate) {
        void *dest;

        PORT_Assert(state->dest == NULL);
        /*
         * We are handling a POINTER or a member of a GROUP, and need to
         * allocate for the data structure.
         */

        dest = sec_asn1d_zalloc(state->top->their_pool,
                                state->theTemplate->size);
        if (dest == NULL) {
            state->top->status = decodeError;
            return;
        }
        state->dest = (char *)dest + state->theTemplate->offset;

        /*
         * For a member of a GROUP, our parent will later put the
         * pointer wherever it belongs.  But for a POINTER, we need
         * to record the destination now, in case notify or filter
         * procs need access to it -- they cannot find it otherwise,
         * until it is too late (for one-pass processing).
         */

        if (state->parent->place == afterPointer) {
            void **placep;

            placep = state->parent->dest;
            *placep = dest;
        }
    }

    /*
     * Remember, length may be indefinite here!  In that case,
     * both contents_length and pending will be zero.
     */

    state->pending = state->contents_length;

    /*
     * An EXPLICIT is nothing but an outer header, which we have
     * already parsed and accepted.  Now we need to do the inner
     * header and its contents.
     */

    if (state->explicit) {
        state->place = afterExplicit;
        state = sec_asn1d_push_state(state->top,
                                     SEC_ASN1GetSubtemplate(state->theTemplate,
                                                            state->dest,
                                                            PR_FALSE),
                                     state->dest, PR_TRUE);
        if (state != NULL) {
            (void)sec_asn1d_init_state_based_on_template(state);
        }
        return;
    }

    /*
     * For GROUP (SET OF, SEQUENCE OF), even if we know the length here
     * we cannot tell how many items we will end up with ... so push a
     * state that can keep track of "children" (the individual members
     * of the group; we will allocate as we go and put them all together
     * at the end.
     */

    if (state->underlying_kind & SEC_ASN1_GROUP) {
        /* XXX If this assertion holds (should be able to confirm it via
         * inspection, too) then move this code into the switch statement
         * below under cases SET_OF and SEQUENCE_OF; it will be cleaner.
         */

        PORT_Assert(state->underlying_kind == SEC_ASN1_SET_OF || state->underlying_kind == SEC_ASN1_SEQUENCE_OF || state->underlying_kind == (SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC) || state->underlying_kind == (SEC_ASN1_SEQUENCE_OF | SEC_ASN1_DYNAMIC));
        if (state->contents_length != 0 || state->indefinite) {
            const SEC_ASN1Template *subt;

            state->place = duringGroup;
            subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->dest,
                                          PR_FALSE);
            state = sec_asn1d_push_state(state->top, subt, NULL, PR_TRUE);
            if (state != NULL) {
                if (!state->top->filter_only)
                    state->allocate = PR_TRUE; /* XXX propogate this? */
                /*
                 * Do the "before" field notification for next in group.
                 */

                sec_asn1d_notify_before(state->top, state->dest, state->depth);
                (void)sec_asn1d_init_state_based_on_template(state);
            }
        } else {
            /*
             * A group of zero; we are done.
             * Set state to afterGroup and let that code plant the NULL.
             */

            state->place = afterGroup;
        }
        return;
    }

    switch (state->underlying_kind) {
        case SEC_ASN1_SEQUENCE:
            /*
             * We need to push a child to handle the individual fields.
             */

            state->place = duringSequence;
            state = sec_asn1d_push_state(state->top, state->theTemplate + 1,
                                         state->dest, PR_TRUE);
            if (state != NULL) {
                /*
                 * Do the "before" field notification.
                 */

                sec_asn1d_notify_before(state->top, state->dest, state->depth);
                (void)sec_asn1d_init_state_based_on_template(state);
            }
            break;

        case SEC_ASN1_SET: /* XXX SET is not really implemented */
            /*
             * XXX A plain SET requires special handling; scanning of a
             * template to see where a field should go (because by definition,
             * they are not in any particular order, and you have to look at
             * each tag to disambiguate what the field is).  We may never
             * implement this because in practice, it seems to be unused.
             */

            PORT_Assert(0);
            PORT_SetError(SEC_ERROR_BAD_DER); /* XXX */
            state->top->status = decodeError;
            break;

        case SEC_ASN1_NULL:
            /*
             * The NULL type, by definition, is "nothing", content length of zero.
             * An indefinite-length encoding is not alloweed.
             */

            if (state->contents_length || state->indefinite) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                break;
            }
            if (state->dest != NULL) {
                item = (SECItem *)(state->dest);
                item->data = NULL;
                item->len = 0;
            }
            state->place = afterEndOfContents;
            break;

        case SEC_ASN1_BMP_STRING:
            /* Error if length is not divisable by 2 */
            if (state->contents_length % 2) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                break;
            }
            /* otherwise, handle as other string types */
            goto regular_string_type;

        case SEC_ASN1_UNIVERSAL_STRING:
            /* Error if length is not divisable by 4 */
            if (state->contents_length % 4) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                break;
            }
            /* otherwise, handle as other string types */
            goto regular_string_type;

        case SEC_ASN1_SKIP:
        case SEC_ASN1_ANY:
        case SEC_ASN1_ANY_CONTENTS:
        /*
         * These are not (necessarily) strings, but they need nearly
         * identical handling (especially when we need to deal with
         * constructed sub-pieces), so we pretend they are.
         */

        /* fallthru */
        regular_string_type:
        case SEC_ASN1_BIT_STRING:
        case SEC_ASN1_IA5_STRING:
        case SEC_ASN1_OCTET_STRING:
        case SEC_ASN1_PRINTABLE_STRING:
        case SEC_ASN1_T61_STRING:
        case SEC_ASN1_UTC_TIME:
        case SEC_ASN1_UTF8_STRING:
        case SEC_ASN1_VISIBLE_STRING:
            /*
             * We are allocating for a primitive or a constructed string.
             * If it is a constructed string, it may also be indefinite-length.
             * If it is primitive, the length can (legally) be zero.
             * Our first order of business is to allocate the memory for
             * the string, if we can (if we know the length).
             */

            item = (SECItem *)(state->dest);

            /*
             * If the item is a definite-length constructed string, then
             * the contents_length is actually larger than what we need
             * (because it also counts each intermediate header which we
             * will be throwing away as we go), but it is a perfectly good
             * upper bound that we just allocate anyway, and then concat
             * as we go; we end up wasting a few extra bytes but save a
             * whole other copy.
             */

            alloc_len = state->contents_length;
            poolp = NULL; /* quiet compiler warnings about unused... */

            if (item == NULL || state->top->filter_only) {
                if (item != NULL) {
                    item->data = NULL;
                    item->len = 0;
                }
                alloc_len = 0;
            } else if (state->substring) {
                /*
                 * If we are a substring of a constructed string, then we may
                 * not have to allocate anything (because our parent, the
                 * actual constructed string, did it for us).  If we are a
                 * substring and we *do* have to allocate, that means our
                 * parent is an indefinite-length, so we allocate from our pool;
                 * later our parent will copy our string into the aggregated
                 * whole and free our pool allocation.
                 */

                if (item->data == NULL) {
                    PORT_Assert(item->len == 0);
                    poolp = state->top->our_pool;
                } else {
                    alloc_len = 0;
                }
            } else {
                item->len = 0;
                item->data = NULL;
                poolp = state->top->their_pool;
            }

            if (alloc_len || ((!state->indefinite) && (state->subitems_head != NULL))) {
                struct subitem *subitem;
                int len;

                PORT_Assert(item);
                if (!item) {
                    PORT_SetError(SEC_ERROR_BAD_DER);
                    state->top->status = decodeError;
                    return;
                }
                PORT_Assert(item->len == 0 && item->data == NULL);
                /*
                 * Check for and handle an ANY which has stashed aside the
                 * header (identifier and length) bytes for us to include
                 * in the saved contents.
                 */

                if (state->subitems_head != NULL) {
                    PORT_Assert(state->underlying_kind == SEC_ASN1_ANY);
                    for (subitem = state->subitems_head;
                         subitem != NULL; subitem = subitem->next)
                        alloc_len += subitem->len;
                }

                if (state->top->max_element_size > 0 &&
                    alloc_len > state->top->max_element_size) {
                    PORT_SetError(SEC_ERROR_OUTPUT_LEN);
                    state->top->status = decodeError;
                    return;
                }

                item->data = (unsigned char *)sec_asn1d_zalloc(poolp, alloc_len);
                if (item->data == NULL) {
                    state->top->status = decodeError;
                    break;
                }

                len = 0;
                for (subitem = state->subitems_head;
                     subitem != NULL; subitem = subitem->next) {
                    PORT_Memcpy(item->data + len, subitem->data, subitem->len);
                    len += subitem->len;
                }
                item->len = len;

                /*
                 * Because we use arenas and have a mark set, we later free
                 * everything we have allocated, so this does *not* present
                 * a memory leak (it is just temporarily left dangling).
                 */

                state->subitems_head = state->subitems_tail = NULL;
            }

            if (state->contents_length == 0 && (!state->indefinite)) {
                /*
                 * A zero-length simple or constructed string; we are done.
                 */

                state->place = afterEndOfContents;
            } else if (state->found_tag_modifiers & SEC_ASN1_CONSTRUCTED) {
                const SEC_ASN1Template *sub;

                switch (state->underlying_kind) {
                    case SEC_ASN1_ANY:
                    case SEC_ASN1_ANY_CONTENTS:
                        sub = SEC_AnyTemplate;
                        break;
                    case SEC_ASN1_BIT_STRING:
                        sub = SEC_BitStringTemplate;
                        break;
                    case SEC_ASN1_BMP_STRING:
                        sub = SEC_BMPStringTemplate;
                        break;
                    case SEC_ASN1_GENERALIZED_TIME:
                        sub = SEC_GeneralizedTimeTemplate;
                        break;
                    case SEC_ASN1_IA5_STRING:
                        sub = SEC_IA5StringTemplate;
                        break;
                    case SEC_ASN1_OCTET_STRING:
                        sub = SEC_OctetStringTemplate;
                        break;
                    case SEC_ASN1_PRINTABLE_STRING:
                        sub = SEC_PrintableStringTemplate;
                        break;
                    case SEC_ASN1_T61_STRING:
                        sub = SEC_T61StringTemplate;
                        break;
                    case SEC_ASN1_UNIVERSAL_STRING:
                        sub = SEC_UniversalStringTemplate;
                        break;
                    case SEC_ASN1_UTC_TIME:
                        sub = SEC_UTCTimeTemplate;
                        break;
                    case SEC_ASN1_UTF8_STRING:
                        sub = SEC_UTF8StringTemplate;
                        break;
                    case SEC_ASN1_VISIBLE_STRING:
                        sub = SEC_VisibleStringTemplate;
                        break;
                    case SEC_ASN1_SKIP:
                        sub = SEC_SkipTemplate;
                        break;
                    default:            /* redundant given outer switch cases, but */
                        PORT_Assert(0); /* the compiler does not seem to know that, */
                        sub = NULL;     /* so just do enough to quiet it. */
                        break;
                }

                state->place = duringConstructedString;
                state = sec_asn1d_push_state(state->top, sub, item, PR_TRUE);
                if (state != NULL) {
                    state->substring = PR_TRUE; /* XXX propogate? */
                    (void)sec_asn1d_init_state_based_on_template(state);
                }
            } else if (state->indefinite) {
                /*
                 * An indefinite-length string *must* be constructed!
                 */

                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
            } else {
                /*
                 * A non-zero-length simple string.
                 */

                if (state->underlying_kind == SEC_ASN1_BIT_STRING)
                    state->place = beforeBitString;
                else
                    state->place = duringLeaf;
            }
            break;

        default:
            /*
             * We are allocating for a simple leaf item.
             */

            if (state->contents_length) {
                if (state->dest != NULL) {
                    item = (SECItem *)(state->dest);
                    item->len = 0;
                    if (state->top->max_element_size > 0 &&
                        state->contents_length > state->top->max_element_size) {
                        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
                        state->top->status = decodeError;
                        return;
                    }

                    if (state->top->filter_only) {
                        item->data = NULL;
                    } else {
                        item->data = (unsigned char *)
                            sec_asn1d_zalloc(state->top->their_pool,
                                             state->contents_length);
                        if (item->data == NULL) {
                            state->top->status = decodeError;
                            return;
                        }
                    }
                }
                state->place = duringLeaf;
            } else {
                /*
                 * An indefinite-length or zero-length item is not allowed.
                 * (All legal cases of such were handled above.)
                 */

                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
            }
    }
}

static void
sec_asn1d_free_child(sec_asn1d_state *state, PRBool error)
{
    if (state->child != NULL) {
        PORT_Assert(error || state->child->consumed == 0);
        PORT_Assert(state->our_mark != NULL);
        PORT_ArenaZRelease(state->top->our_pool, state->our_mark);
        if (error && state->top->their_pool == NULL) {
            /*
             * XXX We need to free anything allocated.
             * At this point, we failed in the middle of decoding. But we
             * can't free the data we previously allocated with PR_Malloc
             * unless we keep track of every pointer. So instead we have a
             * memory leak when decoding fails half-way, unless an arena is
             * used. See bug 95311 .
             */

        }
        state->child = NULL;
        state->our_mark = NULL;
    } else {
        /*
         * It is important that we do not leave a mark unreleased/unmarked.
         * But I do not think we should ever have one set in this case, only
         * if we had a child (handled above).  So check for that.  If this
         * assertion should ever get hit, then we probably need to add code
         * here to release back to our_mark (and then set our_mark to NULL).
         */

        PORT_Assert(state->our_mark == NULL);
    }
    state->place = beforeEndOfContents;
}

/* We have just saved an entire encoded ASN.1 object (type) for a SAVE
** template, and now in the next template, we are going to decode that
** saved data  by calling SEC_ASN1DecoderUpdate recursively.
** If that recursive call fails with needBytes, it is a fatal error,
** because the encoded object should have been complete.
** If that recursive call fails with decodeError, it will have already
** cleaned up the state stack, so we must bail out quickly.
**
** These checks of the status returned by the recursive call are now
** done in the caller of this function, immediately after it returns.
*/

static void
sec_asn1d_reuse_encoding(sec_asn1d_state *state)
{
    sec_asn1d_state *child;
    unsigned long consumed;
    SECItem *item;
    void *dest;

    child = state->child;
    PORT_Assert(child != NULL);

    consumed = child->consumed;
    child->consumed = 0;

    item = (SECItem *)(state->dest);
    PORT_Assert(item != NULL);

    PORT_Assert(item->len == consumed);

    /*
     * Free any grandchild.
     */

    sec_asn1d_free_child(child, PR_FALSE);

    /*
     * Notify after the SAVE field.
     */

    sec_asn1d_notify_after(state->top, state->dest, state->depth);

    /*
     * Adjust to get new dest and move forward.
     */

    dest = (char *)state->dest - state->theTemplate->offset;
    state->theTemplate++;
    child->dest = (char *)dest + state->theTemplate->offset;
    child->theTemplate = state->theTemplate;

    /*
     * Notify before the "real" field.
     */

    PORT_Assert(state->depth == child->depth);
    sec_asn1d_notify_before(state->top, child->dest, child->depth);

    /*
     * This will tell DecoderUpdate to return when it is done.
     */

    state->place = afterSaveEncoding;

    /*
     * We already have a child; "push" it by making it current.
     */

    state->top->current = child;

    /*
     * And initialize it so it is ready to parse.
     */

    (void)sec_asn1d_init_state_based_on_template(child);

    /*
     * Now parse that out of our data.
     */

    if (SEC_ASN1DecoderUpdate(state->top,
                              (char *)item->data, item->len) != SECSuccess)
        return;
    if (state->top->status == needBytes) {
        return;
    }

    PORT_Assert(state->top->current == state);
    PORT_Assert(state->child == child);

    /*
     * That should have consumed what we consumed before.
     */

    PORT_Assert(consumed == child->consumed);
    child->consumed = 0;

    /*
     * Done.
     */

    state->consumed += consumed;
    child->place = notInUse;
    state->place = afterEndOfContents;
}

static unsigned long
sec_asn1d_parse_leaf(sec_asn1d_state *state,
                     const char *buf, unsigned long len)
{
    SECItem *item;
    unsigned long bufLen;

    if (len == 0) {
        state->top->status = needBytes;
        return 0;
    }

    if (state->pending < len)
        len = state->pending;

    bufLen = len;

    item = (SECItem *)(state->dest);
    if (item != NULL && item->data != NULL) {
        unsigned long offset;
        /* Strip leading zeroes when target is unsigned integer */
        if (state->underlying_kind == SEC_ASN1_INTEGER && /* INTEGER   */
            item->len == 0 &&                             /* MSB       */
            item->type == siUnsignedInteger)              /* unsigned  */
        {
            while (len > 1 && buf[0] == 0) { /* leading 0 */
                buf++;
                len--;
            }
        }
        offset = item->len;
        if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
            // The previous bit string must have no unused bits.
            if (item->len & 0x7) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return 0;
            }
            // If this is a bit string, the length is bits, not bytes.
            offset = item->len >> 3;
        }
        if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
            unsigned long len_in_bits;
            // Protect against overflow during the bytes-to-bits conversion.
            if (len >= (ULONG_MAX >> 3) + 1) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return 0;
            }
            len_in_bits = (len << 3) - state->bit_string_unused_bits;
            // Protect against overflow when computing the total length in bits.
            if (UINT_MAX - item->len < len_in_bits) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return 0;
            }
            item->len += len_in_bits;
        } else {
            if (UINT_MAX - item->len < len) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return 0;
            }
            item->len += len;
        }
        PORT_Memcpy(item->data + offset, buf, len);
    }
    state->pending -= bufLen;
    if (state->pending == 0)
        state->place = beforeEndOfContents;

    return bufLen;
}

static unsigned long
sec_asn1d_parse_bit_string(sec_asn1d_state *state,
                           const char *buf, unsigned long len)
{
    unsigned char byte;

    /*PORT_Assert (state->pending > 0); */
    PORT_Assert(state->place == beforeBitString);

    if (state->pending == 0) {
        if (state->dest != NULL) {
            SECItem *item = (SECItem *)(state->dest);
            item->data = NULL;
            item->len = 0;
            state->place = beforeEndOfContents;
            return 0;
        }
    }

    if (len == 0) {
        state->top->status = needBytes;
        return 0;
    }

    byte = (unsigned char)*buf;
    if (byte > 7) {
        PORT_SetError(SEC_ERROR_BAD_DER);
        state->top->status = decodeError;
        return 0;
    }

    state->bit_string_unused_bits = byte;
    state->place = duringBitString;
    state->pending -= 1;

    return 1;
}

static unsigned long
sec_asn1d_parse_more_bit_string(sec_asn1d_state *state,
                                const char *buf, unsigned long len)
{
    PORT_Assert(state->place == duringBitString);
    if (state->pending == 0) {
        /* An empty bit string with some unused bits is invalid. */
        if (state->bit_string_unused_bits) {
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
        } else {
            /* An empty bit string with no unused bits is OK. */
            state->place = beforeEndOfContents;
        }
        return 0;
    }

    len = sec_asn1d_parse_leaf(state, buf, len);
    return len;
}

/*
 * XXX All callers should be looking at return value to detect
 * out-of-memory errors (and stop!).
 */

static struct subitem *
sec_asn1d_add_to_subitems(sec_asn1d_state *state,
                          const void *data, unsigned long len,
                          PRBool copy_data)
{
    struct subitem *thing;

    thing = (struct subitem *)sec_asn1d_zalloc(state->top->our_pool,
                                               sizeof(struct subitem));
    if (thing == NULL) {
        state->top->status = decodeError;
        return NULL;
    }

    if (copy_data) {
        void *copy;
        copy = sec_asn1d_alloc(state->top->our_pool, len);
        if (copy == NULL) {
            state->top->status = decodeError;
            if (!state->top->our_pool)
                PORT_Free(thing);
            return NULL;
        }
        PORT_Memcpy(copy, data, len);
        thing->data = copy;
    } else {
        thing->data = data;
    }
    thing->len = len;
    thing->next = NULL;

    if (state->subitems_head == NULL) {
        PORT_Assert(state->subitems_tail == NULL);
        state->subitems_head = state->subitems_tail = thing;
    } else {
        state->subitems_tail->next = thing;
        state->subitems_tail = thing;
    }

    return thing;
}

static void
sec_asn1d_record_any_header(sec_asn1d_state *state,
                            const char *buf,
                            unsigned long len)
{
    SECItem *item;

    item = (SECItem *)(state->dest);
    if (item != NULL && item->data != NULL) {
        PORT_Assert(state->substring);
        PORT_Memcpy(item->data + item->len, buf, len);
        item->len += len;
    } else {
        sec_asn1d_add_to_subitems(state, buf, len, PR_TRUE);
    }
}

/*
 * We are moving along through the substrings of a constructed string,
 * and have just finished parsing one -- we need to save our child data
 * (if the child was not already writing directly into the destination)
 * and then move forward by one.
 *
 * We also have to detect when we are done:
 *  - a definite-length encoding stops when our pending value hits 0
 *  - an indefinite-length encoding stops when our child is empty
 *    (which means it was the end-of-contents octets)
 */

static void
sec_asn1d_next_substring(sec_asn1d_state *state)
{
    sec_asn1d_state *child;
    SECItem *item;
    unsigned long child_consumed;
    PRBool done;

    PORT_Assert(state->place == duringConstructedString);
    PORT_Assert(state->child != NULL);

    child = state->child;

    child_consumed = child->consumed;
    child->consumed = 0;
    state->consumed += child_consumed;

    done = PR_FALSE;

    if (state->pending) {
        PORT_Assert(!state->indefinite);
        if (child_consumed > state->pending) {
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return;
        }

        state->pending -= child_consumed;
        if (state->pending == 0)
            done = PR_TRUE;
    } else {
        PRBool preallocatedString;
        sec_asn1d_state *temp_state;
        PORT_Assert(state->indefinite);

        item = (SECItem *)(child->dest);

        /**
         * At this point, there's three states at play:
         *   child: The element that was just parsed
         *   state: The currently processed element
         *   'parent' (aka state->parent): The enclosing construct
         *      of state, or NULL if this is the top-most element.
         *
         * This state handles both substrings of a constructed string AND
         * child elements of items whose template type was that of
         * SEC_ASN1_ANY, SEC_ASN1_SAVE, SEC_ASN1_ANY_CONTENTS, SEC_ASN1_SKIP
         * template, as described in sec_asn1d_prepare_for_contents. For
         * brevity, these will be referred to as 'string' and 'any' types.
         *
         * This leads to the following possibilities:
         *   1: This element is an indefinite length string, part of a
         *      definite length string.
         *   2: This element is an indefinite length string, part of an
         *      indefinite length string.
         *   3: This element is an indefinite length any, part of a
         *      definite length any.
         *   4: This element is an indefinite length any, part of an
         *      indefinite length any.
         *   5: This element is an indefinite length any and does not
         *      meet any of the above criteria. Note that this would include
         *      an indefinite length string type matching an indefinite
         *      length any template.
         *
         * In Cases #1 and #3, the definite length 'parent' element will
         * have allocated state->dest based on the parent elements definite
         * size. During the processing of 'child', sec_asn1d_parse_leaf will
         * have copied the (string, any) data directly into the offset of
         * dest, as appropriate, so there's no need for this class to still
         * store the child - it's already been processed.
         *
         * In Cases #2 and #4, dest will be set to the parent element's dest,
         * but dest->data will not have been allocated yet, due to the
         * indefinite length encoding. In this situation, it's necessary to
         * hold onto child (and all other children) until the EOC, at which
         * point, it becomes possible to compute 'state's overall length. Once
         * 'state' has a computed length, this can then be fed to 'parent' (via
         * this state), and then 'parent' can similarly compute the length of
         * all of its children up to the EOC, which will ultimately transit to
         * sec_asn1d_concat_substrings, determine the overall size needed,
         * allocate, and copy the contents (of all of parent's children, which
         * would include 'state', just as 'state' will have copied all of its
         * children via sec_asn1d_concat_substrings)
         *
         * The final case, Case #5, will manifest in that item->data and
         * item->len will be NULL/0, respectively, since this element was
         * indefinite-length encoded. In that case, both the tag and length will
         * already exist in state's subitems, via sec_asn1d_record_any_header,
         * and so the contents (aka 'child') should be added to that list of
         * items to concatenate in sec_asn1d_concat_substrings once the EOC
         * is encountered.
         *
         * To distinguish #2/#4 from #1/#3, it's sufficient to walk the ancestor
         * tree. If the current type is a string type, then the enclosing
         * construct will be that same type (#1/#2). If the current type is an
         * any type, then the enclosing construct is either an any type (#3/#4)
         * or some other type (#5). Since this is BER, this nesting relationship
         * between 'state' and 'parent' may go through several levels of
         * constructed encoding, so continue walking the ancestor chain until a
         * clear determination can be made.
         *
         * The variable preallocatedString is used to indicate Case #1/#3,
         * indicating an in-place copy has already occurred, and Cases #2, #4,
         * and #5 all have the same behaviour of adding a new substring.
         */

        preallocatedString = PR_FALSE;
        temp_state = state;
        while (temp_state && item == temp_state->dest && temp_state->indefinite) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge