Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/lib/util/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 111 kB image not shown  

SSL 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) {
            sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(temp_state);
            if (!parent || parent->underlying_kind != temp_state->underlying_kind) {
                /* Case #5 - Either this is a top-level construct or it is part
                 * of some other element (e.g. a SEQUENCE), in which case, a
                 * new item should be allocated. */

                break;
            }
            if (!parent->indefinite) {
                /* Cases #1 / #3 - A definite length ancestor exists, for which
                 * this is a substring that has already copied into dest. */

                preallocatedString = PR_TRUE;
                break;
            }
            if (!parent->substring) {
                /* Cases #2 / #4 - If the parent is not a substring, but is
                 * indefinite, then there's nothing further up that may have
                 * preallocated dest, thus child will not have already
                 * been copied in place, therefore it's necessary to save child
                 * as a subitem. */

                break;
            }
            temp_state = parent;
        }
        if (item != NULL && item->data != NULL && !preallocatedString) {
            /*
             * Save the string away for later concatenation.
             */

            PORT_Assert(item->data != NULL);
            sec_asn1d_add_to_subitems(state, item->data, item->len, PR_FALSE);
            /*
             * Clear the child item for the next round.
             */

            item->data = NULL;
            item->len = 0;
        }

        /*
         * If our child was just our end-of-contents octets, we are done.
         */

        if (child->endofcontents)
            done = PR_TRUE;
    }

    /*
     * Stop or do the next one.
     */

    if (done) {
        child->place = notInUse;
        state->place = afterConstructedString;
    } else {
        sec_asn1d_scrub_state(child);
        state->top->current = child;
    }
}

/*
 * We are doing a SET OF or SEQUENCE OF, and have just finished an item.
 */

static void
sec_asn1d_next_in_group(sec_asn1d_state *state)
{
    sec_asn1d_state *child;
    unsigned long child_consumed;

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

    child = state->child;

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

    /*
     * If our child was just our end-of-contents octets, we are done.
     */

    if (child->endofcontents) {
        /* XXX I removed the PORT_Assert (child->dest == NULL) because there
         * was a bug in that a template that was a sequence of which also had
         * a child of a sequence of, in an indefinite group was not working
         * properly.  This fix seems to work, (added the if statement below),
         * and nothing appears broken, but I am putting this note here just
         * in case. */

        /*
         * XXX No matter how many times I read that comment,
         * I cannot figure out what case he was fixing.  I believe what he
         * did was deliberate, so I am loathe to touch it.  I need to
         * understand how it could ever be that child->dest != NULL but
         * child->endofcontents is true, and why it is important to check
         * that state->subitems_head is NULL.  This really needs to be
         * figured out, as I am not sure if the following code should be
         * compensating for "offset", as is done a little farther below
         * in the more normal case.
         */

        /*
         * XXX We used to assert our overall state was that we were decoding
         * an indefinite-length object here (state->indefinite == TRUE and no
         * pending bytes in the decoder), but those assertions aren't correct
         * as it's legitimate to wrap indefinite sequences inside definite ones
         * and this code handles that case. Additionally, when compiled in
         * release mode these assertions aren't checked anyway, yet function
         * safely.
         */

        if (child->dest && !state->subitems_head) {
            sec_asn1d_add_to_subitems(state, child->dest, 0, PR_FALSE);
            child->dest = NULL;
        }

        child->place = notInUse;
        state->place = afterGroup;
        return;
    }

    /*
     * Do the "after" field notification for next in group.
     */

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

    /*
     * Save it away (unless we are not storing).
     */

    if (child->dest != NULL) {
        void *dest;

        dest = child->dest;
        dest = (char *)dest - child->theTemplate->offset;
        sec_asn1d_add_to_subitems(state, dest, 0, PR_FALSE);
        child->dest = NULL;
    }

    /*
     * Account for those bytes; see if we are done.
     */

    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) {
            child->place = notInUse;
            state->place = afterGroup;
            return;
        }
    }

    /*
     * Do the "before" field notification for next item in group.
     */

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

    /*
     * Now we do the next one.
     */

    sec_asn1d_scrub_state(child);

    /* Initialize child state from the template */
    sec_asn1d_init_state_based_on_template(child);

    state->top->current = child;
}

/*
 * We are moving along through a sequence; move forward by one,
 * (detecting end-of-sequence when it happens).
 * XXX The handling of "missing" is ugly.  Fix it.
 */

static void
sec_asn1d_next_in_sequence(sec_asn1d_state *state)
{
    sec_asn1d_state *child;
    unsigned long child_consumed;
    PRBool child_missing;

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

    child = state->child;

    /*
     * Do the "after" field notification.
     */

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

    child_missing = (PRBool)child->missing;
    child_consumed = child->consumed;
    child->consumed = 0;

    /*
     * Take care of accounting.
     */

    if (child_missing) {
        PORT_Assert(child->optional);
    } else {
        state->consumed += child_consumed;
        /*
         * Free any grandchild.
         */

        sec_asn1d_free_child(child, 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) {
                child->theTemplate++;
                while (child->theTemplate->kind != 0) {
                    if ((child->theTemplate->kind & SEC_ASN1_OPTIONAL) == 0) {
                        PORT_SetError(SEC_ERROR_BAD_DER);
                        state->top->status = decodeError;
                        return;
                    }
                    child->theTemplate++;
                }
                child->place = notInUse;
                state->place = afterEndOfContents;
                return;
            }
        }
    }

    /*
     * Move forward.
     */

    child->theTemplate++;
    if (child->theTemplate->kind == 0) {
        /*
         * We are done with this sequence.
         */

        child->place = notInUse;
        if (state->pending) {
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
        } else if (child_missing) {
            /*
             * We got to the end, but have a child that started parsing
             * and ended up "missing".  The only legitimate reason for
             * this is that we had one or more optional fields at the
             * end of our sequence, and we were encoded indefinite-length,
             * so when we went looking for those optional fields we
             * found our end-of-contents octets instead.
             * (Yes, this is ugly; dunno a better way to handle it.)
             * So, first confirm the situation, and then mark that we
             * are done.
             */

            if (state->indefinite && child->endofcontents) {
                PORT_Assert(child_consumed == 2);
                if (child_consumed != 2) {
                    PORT_SetError(SEC_ERROR_BAD_DER);
                    state->top->status = decodeError;
                } else {
                    state->consumed += child_consumed;
                    state->place = afterEndOfContents;
                }
            } else {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
            }
        } else {
            /*
             * We have to finish out, maybe reading end-of-contents octets;
             * let the normal logic do the right thing.
             */

            state->place = beforeEndOfContents;
        }
    } else {
        unsigned char child_found_tag_modifiers = 0;
        unsigned long child_found_tag_number = 0;

        /*
         * Reset state and push.
         */

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

        /*
         * Do the "before" field notification.
         */

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

        if (child_missing) { /* if previous child was missing, copy the tag data we already have */
            child_found_tag_modifiers = child->found_tag_modifiers;
            child_found_tag_number = child->found_tag_number;
        }
        state->top->current = child;
        child = sec_asn1d_init_state_based_on_template(child);
        if (child_missing && child) {
            child->place = afterIdentifier;
            child->found_tag_modifiers = child_found_tag_modifiers;
            child->found_tag_number = child_found_tag_number;
            child->consumed = child_consumed;
            if (child->underlying_kind == SEC_ASN1_ANY && !child->top->filter_only) {
                /*
                 * If the new field is an ANY, and we are storing, then
                 * we need to save the tag out.  We would have done this
                 * already in the normal case, but since we were looking
                 * for an optional field, and we did not find it, we only
                 * now realize we need to save the tag.
                 */

                unsigned char identifier;

                /*
                 * Check that we did not end up with a high tag; for that
                 * we need to re-encode the tag into multiple bytes in order
                 * to store it back to look like what we parsed originally.
                 * In practice this does not happen, but for completeness
                 * sake it should probably be made to work at some point.
                 */

                if (child_found_tag_modifiers >= SEC_ASN1_HIGH_TAG_NUMBER) {
                    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
                    state->top->status = decodeError;
                } else {
                    identifier = (unsigned char)(child_found_tag_modifiers | child_found_tag_number);
                    sec_asn1d_record_any_header(child, (char *)&identifier, 1);
                }
            }
        }
    }
}

static void
sec_asn1d_concat_substrings(sec_asn1d_state *state)
{
    PORT_Assert(state->place == afterConstructedString);

    if (state->subitems_head != NULL) {
        struct subitem *substring;
        unsigned long alloc_len, item_len;
        unsigned char *where;
        SECItem *item;
        PRBool is_bit_string;

        item_len = 0;
        is_bit_string = (state->underlying_kind == SEC_ASN1_BIT_STRING)
                            ? PR_TRUE
                            : PR_FALSE;

        substring = state->subitems_head;
        while (substring != NULL) {
            /*
             * All bit-string substrings except the last one should be
             * a clean multiple of 8 bits.
             */

            if (is_bit_string && (substring->next != NULL) && (substring->len & 0x7)) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return;
            }
            item_len += substring->len;
            substring = substring->next;
        }

        if (is_bit_string) {
            alloc_len = ((item_len + 7) >> 3);
        } else {
            /*
             * Add 2 for the end-of-contents octets of an indefinite-length
             * ANY that is *not* also an INNER.  Because we zero-allocate
             * below, all we need to do is increase the length here.
             */

            if (state->underlying_kind == SEC_ASN1_ANY && state->indefinite)
                item_len += 2;
            alloc_len = item_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 = (SECItem *)(state->dest);
        PORT_Assert(item != NULL);
        PORT_Assert(item->data == NULL);
        item->data = (unsigned char *)sec_asn1d_zalloc(state->top->their_pool,
                                                       alloc_len);
        if (item->data == NULL) {
            state->top->status = decodeError;
            return;
        }
        item->len = item_len;

        where = item->data;
        substring = state->subitems_head;
        while (substring != NULL) {
            if (is_bit_string)
                item_len = (substring->len + 7) >> 3;
            else
                item_len = substring->len;
            PORT_Memcpy(where, substring->data, item_len);
            where += item_len;
            substring = substring->next;
        }

        /*
         * 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;
    }

    state->place = afterEndOfContents;
}

static void
sec_asn1d_concat_group(sec_asn1d_state *state)
{
    const void ***placep;

    PORT_Assert(state->place == afterGroup);

    placep = (const void ***)state->dest;
    PORT_Assert(state->subitems_head == NULL || placep != NULL);
    if (placep != NULL) {
        struct subitem *item;
        const void **group;
        int count;

        count = 0;
        item = state->subitems_head;
        while (item != NULL) {
            PORT_Assert(item->next != NULL || item == state->subitems_tail);
            count++;
            item = item->next;
        }

        group = (const void **)sec_asn1d_zalloc(state->top->their_pool,
                                                (count + 1) * (sizeof(void *)));
        if (group == NULL) {
            state->top->status = decodeError;
            return;
        }

        *placep = group;

        item = state->subitems_head;
        while (item != NULL) {
            *group++ = item->data;
            item = item->next;
        }
        *group = NULL;

        /*
         * 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;
    }

    state->place = afterEndOfContents;
}

/*
 * For those states that push a child to handle a subtemplate,
 * "absorb" that child (transfer necessary information).
 */

static void
sec_asn1d_absorb_child(sec_asn1d_state *state)
{
    /*
     * There is absolutely supposed to be a child there.
     */

    PORT_Assert(state->child != NULL);

    /*
     * Inherit the missing status of our child, and do the ugly
     * backing-up if necessary.
     */

    state->missing = state->child->missing;
    if (state->missing) {
        state->found_tag_number = state->child->found_tag_number;
        state->found_tag_modifiers = state->child->found_tag_modifiers;
        state->endofcontents = state->child->endofcontents;
    }

    /*
     * Add in number of bytes consumed by child.
     * (Only EXPLICIT should have already consumed bytes itself.)
     */

    PORT_Assert(state->place == afterExplicit || state->consumed == 0);
    state->consumed += state->child->consumed;

    /*
     * Subtract from bytes pending; this only applies to a definite-length
     * EXPLICIT field.
     */

    if (state->pending) {
        PORT_Assert(!state->indefinite);
        PORT_Assert(state->place == afterExplicit);

        /*
         * If we had a definite-length explicit, then what the child
         * consumed should be what was left pending.
         */

        if (state->pending != state->child->consumed) {
            if (state->pending < state->child->consumed) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return;
            }
            /*
             * Okay, this is a hack.  It *should* be an error whether
             * pending is too big or too small, but it turns out that
             * we had a bug in our *old* DER encoder that ended up
             * counting an explicit header twice in the case where
             * the underlying type was an ANY.  So, because we cannot
             * prevent receiving these (our own certificate server can
             * send them to us), we need to be lenient and accept them.
             * To do so, we need to pretend as if we read all of the
             * bytes that the header said we would find, even though
             * we actually came up short.
             */

            state->consumed += (state->pending - state->child->consumed);
        }
        state->pending = 0;
    }

    /*
     * Indicate that we are done with child.
     */

    state->child->consumed = 0;

    /*
     * And move on to final state.
     * (Technically everybody could move to afterEndOfContents except
     * for an indefinite-length EXPLICIT; for simplicity though we assert
     * that but let the end-of-contents code do the real determination.)
     */

    PORT_Assert(state->place == afterExplicit || (!state->indefinite));
    state->place = beforeEndOfContents;
}

static void
sec_asn1d_prepare_for_end_of_contents(sec_asn1d_state *state)
{
    PORT_Assert(state->place == beforeEndOfContents);

    if (state->indefinite) {
        state->place = duringEndOfContents;
        state->pending = 2;
    } else {
        state->place = afterEndOfContents;
    }
}

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

    PORT_Assert(state->pending <= 2);
    PORT_Assert(state->place == duringEndOfContents);

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

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

    for (i = 0; i < len; i++) {
        if (buf[i] != 0) {
            /*
             * We expect to find only zeros; if not, just give up.
             */

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

    state->pending -= len;

    if (state->pending == 0) {
        state->place = afterEndOfContents;
        /* These end-of-contents octets either terminate a SEQUENCE, a GROUP,
         * or a constructed string. The SEQUENCE case is unique in that the
         * state parses its own end-of-contents octets and therefore should not
         * have its `endofcontents` flag set. We identify the SEQUENCE case by
         * checking whether the child state's template is pointing at a
         * template terminator (see `sec_asn1d_next_in_sequence`).
         */

        if (state->child && state->child->theTemplate->kind == 0) {
            state->endofcontents = PR_FALSE;
        } else {
            state->endofcontents = PR_TRUE;
        }
    }

    return len;
}

static void
sec_asn1d_pop_state(sec_asn1d_state *state)
{
#if 0  /* XXX I think this should always be handled explicitly by parent? */
    /*
     * Account for our child.
     */

    if (state->child != NULL) {
    state->consumed += state->child->consumed;
    if (state->pending) {
        PORT_Assert (!state->indefinite);
        if (state->child->consumed > state->pending) {
        PORT_SetError (SEC_ERROR_BAD_DER);
        state->top->status = decodeError;
        } else {
        state->pending -= state->child->consumed;
        }
    }
    state->child->consumed = 0;
    }
#endif /* XXX */

    /*
     * Free our child.
     */

    sec_asn1d_free_child(state, PR_FALSE);

    /*
     * Just make my parent be the current state.  It will then clean
     * up after me and free me (or reuse me).
     */

    state->top->current = state->parent;
}

static sec_asn1d_state *
sec_asn1d_before_choice(sec_asn1d_state *state)
{
    sec_asn1d_state *child;

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

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

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

    child = sec_asn1d_push_state(state->top, state->theTemplate + 1,
                                 (char *)state->dest - state->theTemplate->offset,
                                 PR_FALSE);
    if ((sec_asn1d_state *)NULL == child) {
        return (sec_asn1d_state *)NULL;
    }

    sec_asn1d_scrub_state(child);
    child = sec_asn1d_init_state_based_on_template(child);
    if ((sec_asn1d_state *)NULL == child) {
        return (sec_asn1d_state *)NULL;
    }

    child->optional = PR_TRUE;

    state->place = duringChoice;

    return child;
}

static sec_asn1d_state *
sec_asn1d_during_choice(sec_asn1d_state *state)
{
    sec_asn1d_state *child = state->child;

    PORT_Assert((sec_asn1d_state *)NULL != child);

    if (child->missing) {
        unsigned char child_found_tag_modifiers = 0;
        unsigned long child_found_tag_number = 0;
        void *dest;

        state->consumed += child->consumed;

        if (child->endofcontents) {
            /* This choice is probably the first item in a GROUP
            ** (e.g. SET_OF) that was indefinite-length encoded.
            ** We're actually at the end of that GROUP.
            ** We look up the stack to be sure that we find
            ** a state with indefinite length encoding before we
            ** find a state (like a SEQUENCE) that is definite.
            */

            child->place = notInUse;
            state->place = afterChoice;
            state->endofcontents = PR_TRUE; /* propagate this up */
            if (sec_asn1d_parent_allows_EOC(state))
                return state;
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return NULL;
        }

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

        if (0 == child->theTemplate->kind) {
            /* Ran out of choices */
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return (sec_asn1d_state *)NULL;
        }
        child->dest = (char *)dest + child->theTemplate->offset;

        /* cargo'd from next_in_sequence innards */
        if (state->pending) {
            PORT_Assert(!state->indefinite);
            if (child->consumed > state->pending) {
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return NULL;
            }
            state->pending -= child->consumed;
            if (0 == state->pending) {
                /* XXX uh.. not sure if I should have stopped this
                 * from happening before. */

                PORT_Assert(0);
                PORT_SetError(SEC_ERROR_BAD_DER);
                state->top->status = decodeError;
                return (sec_asn1d_state *)NULL;
            }
        }

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

        /* move it on top again */
        state->top->current = child;

        child_found_tag_modifiers = child->found_tag_modifiers;
        child_found_tag_number = child->found_tag_number;

        child = sec_asn1d_init_state_based_on_template(child);
        if ((sec_asn1d_state *)NULL == child) {
            return (sec_asn1d_state *)NULL;
        }

        /* copy our findings to the new top */
        child->found_tag_modifiers = child_found_tag_modifiers;
        child->found_tag_number = child_found_tag_number;

        child->optional = PR_TRUE;
        child->place = afterIdentifier;

        return child;
    }
    if ((void *)NULL != state->dest) {
        /* Store the enum */
        int *which = (int *)state->dest;
        *which = (int)child->theTemplate->size;
    }

    child->place = notInUse;

    state->place = afterChoice;
    return state;
}

static void
sec_asn1d_after_choice(sec_asn1d_state *state)
{
    state->consumed += state->child->consumed;
    state->child->consumed = 0;
    state->place = afterEndOfContents;
    sec_asn1d_pop_state(state);
}

unsigned long
sec_asn1d_uinteger(SECItem *src)
{
    unsigned long value;
    int len;

    if (src->len > 5 || (src->len > 4 && src->data[0] == 0))
        return 0;

    value = 0;
    len = src->len;
    while (len) {
        value <<= 8;
        value |= src->data[--len];
    }
    return value;
}

SECStatus
SEC_ASN1DecodeInteger(SECItem *src, unsigned long *value)
{
    unsigned long v;
    unsigned int i;

    if (src == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (src->len > sizeof(unsigned long)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (src->data == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (src->data[0] & 0x80)
        v = -1; /* signed and negative - start with all 1's */
    else
        v = 0;

    for (i = 0; i < src->len; i++) {
        /* shift in next byte */
        v <<= 8;
        v |= src->data[i];
    }
    *value = v;
    return SECSuccess;
}

#ifdef DEBUG_ASN1D_STATES
static void
dump_states(SEC_ASN1DecoderContext *cx)
{
    sec_asn1d_state *state;
    int bufsize = 256;
    char kindBuf[bufsize];

    for (state = cx->current; state->parent; state = state->parent) {
        ;
    }

    for (; state; state = state->child) {
        int i;
        for (i = 0; i < state->depth; i++) {
            printf("  ");
        }

        i = formatKind(state->theTemplate->kind, kindBuf, bufsize);
        printf("%s: tmpl kind %s",
               (state == cx->current) ? "STATE" : "State",
               kindBuf);
        printf(" %s", (state->place >= 0 && state->place <= notInUse) ? place_names[state->place] : "(undefined)");
        if (!i)
            printf(", expect 0x%02lx",
                   state->expect_tag_number | state->expect_tag_modifiers);

        printf("%s%s%s %lu\n",
               state->indefinite ? ", indef" : "",
               state->missing ? ", miss" : "",
               state->endofcontents ? ", EOC" : "",
               state->pending);
    }

    return;
}
#endif /* DEBUG_ASN1D_STATES */

SECStatus
SEC_ASN1DecoderUpdate(SEC_ASN1DecoderContext *cx,
                      const char *buf, unsigned long len)
{
    sec_asn1d_state *state = NULL;
    unsigned long consumed;
    SEC_ASN1EncodingPart what;

    if (cx->status == needBytes)
        cx->status = keepGoing;

    while (cx->status == keepGoing) {
        state = cx->current;
        what = SEC_ASN1_Contents;
        consumed = 0;
#ifdef DEBUG_ASN1D_STATES
        printf("\nPLACE = %s, next byte = 0x%02x, %p[%lu]\n",
               (state->place >= 0 && state->place <= notInUse) ? place_names[state->place] : "(undefined)",
               len ? (unsigned int)((unsigned char *)buf)[consumed] : 0,
               buf, consumed);
        dump_states(cx);
#endif /* DEBUG_ASN1D_STATES */
        switch (state->place) {
            case beforeIdentifier:
                consumed = sec_asn1d_parse_identifier(state, buf, len);
                what = SEC_ASN1_Identifier;
                break;
            case duringIdentifier:
                consumed = sec_asn1d_parse_more_identifier(state, buf, len);
                what = SEC_ASN1_Identifier;
                break;
            case afterIdentifier:
                sec_asn1d_confirm_identifier(state);
                break;
            case beforeLength:
                consumed = sec_asn1d_parse_length(state, buf, len);
                what = SEC_ASN1_Length;
                break;
            case duringLength:
                consumed = sec_asn1d_parse_more_length(state, buf, len);
                what = SEC_ASN1_Length;
                break;
            case afterLength:
                sec_asn1d_prepare_for_contents(state);
                break;
            case beforeBitString:
                consumed = sec_asn1d_parse_bit_string(state, buf, len);
                break;
            case duringBitString:
                consumed = sec_asn1d_parse_more_bit_string(state, buf, len);
                break;
            case duringConstructedString:
                sec_asn1d_next_substring(state);
                break;
            case duringGroup:
                sec_asn1d_next_in_group(state);
                break;
            case duringLeaf:
                consumed = sec_asn1d_parse_leaf(state, buf, len);
                break;
            case duringSaveEncoding:
                sec_asn1d_reuse_encoding(state);
                if (cx->status == decodeError) {
                    /* recursive call has already popped all states from stack.
                    ** Bail out quickly.
                    */

                    return SECFailure;
                }
                if (cx->status == needBytes) {
                    /* recursive call wanted more data. Fatal. Clean up below. */
                    PORT_SetError(SEC_ERROR_BAD_DER);
                    cx->status = decodeError;
                }
                break;
            case duringSequence:
                sec_asn1d_next_in_sequence(state);
                break;
            case afterConstructedString:
                sec_asn1d_concat_substrings(state);
                break;
            case afterExplicit:
            case afterImplicit:
            case afterInline:
            case afterPointer:
                sec_asn1d_absorb_child(state);
                break;
            case afterGroup:
                sec_asn1d_concat_group(state);
                break;
            case afterSaveEncoding:
                /* SEC_ASN1DecoderUpdate has called itself recursively to
                ** decode SAVEd encoded data, and now is done decoding that.
                ** Return to the calling copy of SEC_ASN1DecoderUpdate.
                */

                return SECSuccess;
            case beforeEndOfContents:
                sec_asn1d_prepare_for_end_of_contents(state);
                break;
            case duringEndOfContents:
                consumed = sec_asn1d_parse_end_of_contents(state, buf, len);
                what = SEC_ASN1_EndOfContents;
                break;
            case afterEndOfContents:
                sec_asn1d_pop_state(state);
                break;
            case beforeChoice:
                state = sec_asn1d_before_choice(state);
                break;
            case duringChoice:
                state = sec_asn1d_during_choice(state);
                break;
            case afterChoice:
                sec_asn1d_after_choice(state);
                break;
            case notInUse:
            default:
                /* This is not an error, but rather a plain old BUG! */
                PORT_Assert(0);
                PORT_SetError(SEC_ERROR_BAD_DER);
                cx->status = decodeError;
                break;
        }

        if (cx->status == decodeError)
            break;

        /* We should not consume more than we have.  */
        PORT_Assert(consumed <= len);
        if (consumed > len) {
            PORT_SetError(SEC_ERROR_BAD_DER);
            cx->status = decodeError;
            break;
        }

        /* It might have changed, so we have to update our local copy.  */
        state = cx->current;

        /* If it is NULL, we have popped all the way to the top.  */
        if (state == NULL) {
            PORT_Assert(consumed == 0);
#if 0 /* XXX I want this here, but it seems that we have situations (like \
       * downloading a pkcs7 cert chain from some issuers) that give us a \
       * length which is greater than the entire encoding.  So, we cannot \
       * have this be an error.                                           \
       */

        if (len > 0) {
        PORT_SetError (SEC_ERROR_BAD_DER);
        cx->status = decodeError;
        } else
#endif
            cx->status = allDone;
            break;
        } else if (state->theTemplate->kind == SEC_ASN1_SKIP_REST) {
            cx->status = allDone;
            break;
        }

        if (consumed == 0)
            continue;

        /*
         * The following check is specifically looking for an ANY
         * that is *not* also an INNER, because we need to save aside
         * all bytes in that case -- the contents parts will get
         * handled like all other contents, and the end-of-contents
         * bytes are added by the concat code, but the outer header
         * bytes need to get saved too, so we do them explicitly here.
         */

        if (state->underlying_kind == SEC_ASN1_ANY && !cx->filter_only && (what == SEC_ASN1_Identifier || what == SEC_ASN1_Length)) {
            sec_asn1d_record_any_header(state, buf, consumed);
        }

        /*
         * We had some number of good, accepted bytes.  If the caller
         * has registered to see them, pass them along.
         */

        if (state->top->filter_proc != NULL) {
            int depth;

            depth = state->depth;
            if (what == SEC_ASN1_EndOfContents && !state->indefinite) {
                PORT_Assert(state->parent != NULL && state->parent->indefinite);
                depth--;
                PORT_Assert(depth == state->parent->depth);
            }
            (*state->top->filter_proc)(state->top->filter_arg,
                                       buf, consumed, depth, what);
        }

        state->consumed += consumed;
        buf += consumed;
        len -= consumed;
    }

    if (cx->status == decodeError) {
        while (state != NULL) {
            sec_asn1d_free_child(state, PR_TRUE);
            state = state->parent;
        }
#ifdef SEC_ASN1D_FREE_ON_ERROR /*                                           \
                                * XXX This does not work because we can     \
                                * end up leaving behind dangling pointers   \
                                * to stuff that was allocated.  In order    \
                                * to make this really work (which would     \
                                * be a good thing, I think), we need to     \
                                * keep track of every place/pointer that    \
                                * was allocated and make sure to NULL it    \
                                * out before we then free back to the mark. \
                                */

        if (cx->their_pool != NULL) {
            PORT_Assert(cx->their_mark != NULL);
            PORT_ArenaRelease(cx->their_pool, cx->their_mark);
            cx->their_mark = NULL;
        }
#endif
        return SECFailure;
    }

#if 0 /* XXX This is what I want, but cannot have because it seems we    \
       * have situations (like when downloading a pkcs7 cert chain from  \
       * some issuers) that give us a total length which is greater than \
       * the entire encoding.  So, we have to allow allDone to have a    \
       * remaining length greater than zero.  I wanted to catch internal \
       * bugs with this, noticing when we do not have the right length.  \
       * Oh well.                                                        \
       */

    PORT_Assert (len == 0
         && (cx->status == needBytes || cx->status == allDone));
#else
    PORT_Assert((len == 0 && cx->status == needBytes) || cx->status == allDone);
#endif
    return SECSuccess;
}

SECStatus
SEC_ASN1DecoderFinish(SEC_ASN1DecoderContext *cx)
{
    SECStatus rv;

    if (!cx || cx->status == needBytes) {
        PORT_SetError(SEC_ERROR_BAD_DER);
        rv = SECFailure;
    } else {
        rv = SECSuccess;
    }

    /*
     * XXX anything else that needs to be finished?
     */


    if (cx) {
        PORT_FreeArena(cx->our_pool, PR_TRUE);
    }

    return rv;
}

SEC_ASN1DecoderContext *
SEC_ASN1DecoderStart(PLArenaPool *their_pool, void *dest,
                     const SEC_ASN1Template *theTemplate)
{
    PLArenaPool *our_pool;
    SEC_ASN1DecoderContext *cx;

    our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    if (our_pool == NULL)
        return NULL;

    cx = (SEC_ASN1DecoderContext *)PORT_ArenaZAlloc(our_pool, sizeof(*cx));
    if (cx == NULL) {
        PORT_FreeArena(our_pool, PR_FALSE);
        return NULL;
    }

    cx->our_pool = our_pool;
    if (their_pool != NULL) {
        cx->their_pool = their_pool;
#ifdef SEC_ASN1D_FREE_ON_ERROR
        cx->their_mark = PORT_ArenaMark(their_pool);
#endif
    }

    cx->status = needBytes;

    if (sec_asn1d_push_state(cx, theTemplate, dest, PR_FALSE) == NULL || sec_asn1d_init_state_based_on_template(cx->current) == NULL) {
        /*
         * Trouble initializing (probably due to failed allocations)
         * requires that we just give up.
         */

        PORT_FreeArena(our_pool, PR_FALSE);
        return NULL;
    }

    return cx;
}

void
SEC_ASN1DecoderSetFilterProc(SEC_ASN1DecoderContext *cx,
                             SEC_ASN1WriteProc fn, void *arg,
                             PRBool only)
{
    /* check that we are "between" fields here */
    PORT_Assert(cx->during_notify);

    cx->filter_proc = fn;
    cx->filter_arg = arg;
    cx->filter_only = only;
}

void
SEC_ASN1DecoderClearFilterProc(SEC_ASN1DecoderContext *cx)
{
    /* check that we are "between" fields here */
    PORT_Assert(cx->during_notify);

    cx->filter_proc = NULL;
    cx->filter_arg = NULL;
    cx->filter_only = PR_FALSE;
}

void
SEC_ASN1DecoderSetNotifyProc(SEC_ASN1DecoderContext *cx,
                             SEC_ASN1NotifyProc fn, void *arg)
{
    cx->notify_proc = fn;
    cx->notify_arg = arg;
}

void
SEC_ASN1DecoderClearNotifyProc(SEC_ASN1DecoderContext *cx)
{
    cx->notify_proc = NULL;
    cx->notify_arg = NULL; /* not necessary; just being clean */
}

void
SEC_ASN1DecoderSetMaximumElementSize(SEC_ASN1DecoderContext *cx,
                                     unsigned long max_size)
{
    cx->max_element_size = max_size;
}

void
SEC_ASN1DecoderAbort(SEC_ASN1DecoderContext *cx, int error)
{
    PORT_Assert(cx);
    PORT_SetError(error);
    cx->status = decodeError;
}

SECStatus
SEC_ASN1Decode(PLArenaPool *poolp, void *dest,
               const SEC_ASN1Template *theTemplate,
               const char *buf, long len)
{
    SEC_ASN1DecoderContext *dcx;
    SECStatus urv, frv;

    dcx = SEC_ASN1DecoderStart(poolp, dest, theTemplate);
    if (dcx == NULL)
        return SECFailure;

    /* In one-shot mode, there's no possibility of streaming data beyond the
     * length of len */

    SEC_ASN1DecoderSetMaximumElementSize(dcx, len);

    urv = SEC_ASN1DecoderUpdate(dcx, buf, len);
    frv = SEC_ASN1DecoderFinish(dcx);

    if (urv != SECSuccess)
        return urv;

    return frv;
}

SECStatus
SEC_ASN1DecodeItem(PLArenaPool *poolp, void *dest,
                   const SEC_ASN1Template *theTemplate,
                   const SECItem *src)
{
    return SEC_ASN1Decode(poolp, dest, theTemplate,
                          (const char *)src->data, src->len);
}

#ifdef DEBUG_ASN1D_STATES
void
sec_asn1d_Assert(const char *s, const char *file, PRIntn ln)
{
    printf("Assertion failed, \"%s\", file %s, line %d\n", s, file, ln);
    fflush(stdout);
}
#endif

/*
 * Generic templates for individual/simple items and pointers to
 * and sets of same.
 *
 * If you need to add a new one, please note the following:
 *   - For each new basic type you should add *four* templates:
 *  one plain, one PointerTo, one SequenceOf and one SetOf.
 *   - If the new type can be constructed (meaning, it is a
 *  *string* type according to BER/DER rules), then you should
 *  or-in SEC_ASN1_MAY_STREAM to the type in the basic template.
 *  See the definition of the OctetString template for an example.
 *   - It may not be obvious, but these are in *alphabetical*
 *  order based on the SEC_ASN1_XXX name; so put new ones in
 *  the appropriate place.
 */


const SEC_ASN1Template SEC_SequenceOfAnyTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate }
};

#if 0

const SEC_ASN1Template SEC_PointerToBitStringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_BitStringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfBitStringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_BitStringTemplate }
};

const SEC_ASN1Template SEC_SetOfBitStringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_BitStringTemplate }
};

const SEC_ASN1Template SEC_PointerToBMPStringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_BMPStringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfBMPStringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_BMPStringTemplate }
};

const SEC_ASN1Template SEC_SetOfBMPStringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_BMPStringTemplate }
};

const SEC_ASN1Template SEC_PointerToBooleanTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_BooleanTemplate }
};

const SEC_ASN1Template SEC_SequenceOfBooleanTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_BooleanTemplate }
};

const SEC_ASN1Template SEC_SetOfBooleanTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_BooleanTemplate }
};

#endif

const SEC_ASN1Template SEC_EnumeratedTemplate[] = {
    { SEC_ASN1_ENUMERATED, 0, NULL, sizeof(SECItem) }
};

const SEC_ASN1Template SEC_PointerToEnumeratedTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_EnumeratedTemplate }
};

#if 0

const SEC_ASN1Template SEC_SequenceOfEnumeratedTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_EnumeratedTemplate }
};

#endif

const SEC_ASN1Template SEC_SetOfEnumeratedTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_EnumeratedTemplate }
};

const SEC_ASN1Template SEC_PointerToGeneralizedTimeTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_GeneralizedTimeTemplate }
};

#if 0

const SEC_ASN1Template SEC_SequenceOfGeneralizedTimeTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_GeneralizedTimeTemplate }
};

const SEC_ASN1Template SEC_SetOfGeneralizedTimeTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_GeneralizedTimeTemplate }
};

const SEC_ASN1Template SEC_PointerToIA5StringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_IA5StringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfIA5StringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_IA5StringTemplate }
};

const SEC_ASN1Template SEC_SetOfIA5StringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_IA5StringTemplate }
};

const SEC_ASN1Template SEC_PointerToIntegerTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_IntegerTemplate }
};

const SEC_ASN1Template SEC_SequenceOfIntegerTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_IntegerTemplate }
};

const SEC_ASN1Template SEC_SetOfIntegerTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_IntegerTemplate }
};

const SEC_ASN1Template SEC_PointerToNullTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_NullTemplate }
};

const SEC_ASN1Template SEC_SequenceOfNullTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_NullTemplate }
};

const SEC_ASN1Template SEC_SetOfNullTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_NullTemplate }
};

const SEC_ASN1Template SEC_PointerToObjectIDTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_ObjectIDTemplate }
};

#endif

const SEC_ASN1Template SEC_SequenceOfObjectIDTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_ObjectIDTemplate }
};

#if 0

const SEC_ASN1Template SEC_SetOfObjectIDTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_ObjectIDTemplate }
};

const SEC_ASN1Template SEC_SequenceOfOctetStringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_OctetStringTemplate }
};

const SEC_ASN1Template SEC_SetOfOctetStringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_OctetStringTemplate }
};

#endif

const SEC_ASN1Template SEC_PrintableStringTemplate[] = {
    { SEC_ASN1_PRINTABLE_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) }
};

#if 0

const SEC_ASN1Template SEC_PointerToPrintableStringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_PrintableStringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfPrintableStringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_PrintableStringTemplate }
};

const SEC_ASN1Template SEC_SetOfPrintableStringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_PrintableStringTemplate }
};

#endif

const SEC_ASN1Template SEC_T61StringTemplate[] = {
    { SEC_ASN1_T61_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) }
};

#if 0

const SEC_ASN1Template SEC_PointerToT61StringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_T61StringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfT61StringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_T61StringTemplate }
};

const SEC_ASN1Template SEC_SetOfT61StringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_T61StringTemplate }
};

#endif

const SEC_ASN1Template SEC_UniversalStringTemplate[] = {
    { SEC_ASN1_UNIVERSAL_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) }
};

#if 0

const SEC_ASN1Template SEC_PointerToUniversalStringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_UniversalStringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfUniversalStringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_UniversalStringTemplate }
};

const SEC_ASN1Template SEC_SetOfUniversalStringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_UniversalStringTemplate }
};

const SEC_ASN1Template SEC_PointerToUTCTimeTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_UTCTimeTemplate }
};

const SEC_ASN1Template SEC_SequenceOfUTCTimeTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_UTCTimeTemplate }
};

const SEC_ASN1Template SEC_SetOfUTCTimeTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_UTCTimeTemplate }
};

const SEC_ASN1Template SEC_PointerToUTF8StringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_UTF8StringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfUTF8StringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_UTF8StringTemplate }
};

const SEC_ASN1Template SEC_SetOfUTF8StringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_UTF8StringTemplate }
};

#endif

const SEC_ASN1Template SEC_VisibleStringTemplate[] = {
    { SEC_ASN1_VISIBLE_STRING | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) }
};

#if 0

const SEC_ASN1Template SEC_PointerToVisibleStringTemplate[] = {
    { SEC_ASN1_POINTER, 0, SEC_VisibleStringTemplate }
};

const SEC_ASN1Template SEC_SequenceOfVisibleStringTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_VisibleStringTemplate }
};

const SEC_ASN1Template SEC_SetOfVisibleStringTemplate[] = {
    { SEC_ASN1_SET_OF, 0, SEC_VisibleStringTemplate }
};

#endif

/*
 * Template for skipping a subitem.
 *
 * Note that it only makes sense to use this for decoding (when you want
 * to decode something where you are only interested in one or two of
 * the fields); you cannot encode a SKIP!
 */

const SEC_ASN1Template SEC_SkipTemplate[] = {
    { SEC_ASN1_SKIP }
};

/* These functions simply return the address of the above-declared templates.
** This is necessary for Windows DLLs.  Sigh.
*/

SEC_ASN1_CHOOSER_IMPLEMENT(SEC_EnumeratedTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PointerToEnumeratedTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SequenceOfAnyTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SequenceOfObjectIDTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SkipTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_UniversalStringTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PrintableStringTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_T61StringTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_PointerToGeneralizedTimeTemplate)

Messung V0.5 in Prozent
C=93 H=93 G=92

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.78Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-26) ¤

*Eine klare Vorstellung vom Zielzustand






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.