/* 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);
                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) {
            PORT_SetError(SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return;
        }
        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;
    }

    char *dest = state->dest ? (char *)state->dest - state->theTemplate->offset : NULL;

    child = sec_asn1d_push_state(state->top, state->theTemplate + 1,
                                 dest,
                                 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 = child->dest ? (char *)child->dest - child->theTemplate->offset : NULL;
        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 = dest ? (char *)dest + child->theTemplate->offset : NULL;

        /* 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 <= 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 <= 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) {
                depth--;
                PORT_Assert(depth == sec_asn1d_get_enclosing_construct(state)->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) {
        if (0 == PORT_GetError()) {
            /* don't clobber a real reason for the failure like bad password
             * or invalid algorithm */
            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)
