secasn1d.c   [plain text]


/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Netscape security libraries.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are 
 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
 * Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

/*
 * Support for DEcoding ASN.1 data based on BER/DER (Basic/Distinguished
 * Encoding Rules).
 *
 * $Id: secasn1d.c,v 1.16 2004/05/13 15:29:13 dmitch Exp $
 */

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

#ifdef	NDEBUG
#define DEBUG_DECASN1	0
#else
#define DEBUG_DECASN1	1
#endif

#if		DEBUG_DECASN1
#include <stdio.h>
#define dprintf(args...)			printf(args)
#else
#define dprintf(args...)
#endif	/* DEBUG_DECASN1 */

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;

#ifndef	NDEBUG
#define DEBUG_ASN1D_STATES	1
/* tweakable by debugger, debug only */
int doDumpStates = 0;
#else	/* DEBUG_ASN1D_STATES 0 */
#endif	/* DEBUG_ASN1D_STATES */

#if DEBUG_ASN1D_STATES
static const char *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 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) {
        sprintf(buf, " %s", class_names[(kind & SEC_ASN1_CLASS_MASK) >> 6] );
        buf += strlen(buf);
    }
    if (kind & SEC_ASN1_METHOD_MASK) {
        sprintf(buf, " %s", method_names[1]);
        buf += strlen(buf);
    }
    if ((kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL) {
        if (k || !notag) {
            sprintf(buf, " %s", type_names[k] );
            if ((k == SEC_ASN1_SET || k == SEC_ASN1_SEQUENCE) &&
                (kind & SEC_ASN1_GROUP)) {
                buf += strlen(buf);
                sprintf(buf, "_OF");
            }
        }
    } else {
        sprintf(buf, " [%lu]", k);
    }
    buf += strlen(buf);

    for (k = kind >> 8, i = 0; k; k >>= 1, ++i) {
        if (k & 1) {
            sprintf(buf, " %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 SecAsn1Template *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 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 {
    PRArenaPool *our_pool;		/* for our internal allocs */
    PRArenaPool *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;

    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 (PRArenaPool *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 (PRArenaPool *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 SecAsn1Template *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);
    }

    new_state = (sec_asn1d_state*)sec_asn1d_zalloc (cx->our_pool, 
						    sizeof(*new_state));
    if (new_state == NULL) {
        dprintf("decodeError: zalloc failure\n");
        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 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 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,
	#ifdef	__APPLE__
	const char *buf		/* for SEC_ASN1GetSubtemplate() */
	#endif
	)
{
    PRBool explicit, optional, universal;
    unsigned char expect_tag_modifiers;
    unsigned long encode_kind, under_kind;
    unsigned long check_tag_mask, expect_tag_number;
	#ifdef	__APPLE__
	unsigned long dynamic;
	#endif


    /* 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, kSecAsn1AnyTemplate,
						state->dest, PR_FALSE);
			if (state != NULL)
				state = sec_asn1d_init_state_based_on_template (state,
					buf /* __APPLE__ */);
			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;

	#ifdef	__APPLE__
	dynamic = (encode_kind & SEC_ASN1_DYNAMIC) ? PR_TRUE : PR_FALSE;
    encode_kind &= ~SEC_ASN1_DYNAMIC;
	#endif

    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 SecAsn1Template *subt;
		void *dest;
		PRBool child_allocate;
		void *subDest;
		
		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 */
				/* FIXME - why are optional and inline mutually 
				 * exclusive? Delete this assert and see what happens...
				PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional);
				*/
				state->place = afterInline;
			} else {
				state->place = afterImplicit;
			}
		}
	
		state->optional = optional;
		
		subDest = state->dest;
		#if defined(__APPLE__)
		/*
		 * We might be starting the processing of a group or a
		 * set, in which case state->dest is NULL. Get parent's dest,
		 * or grandparent's, etc... just for the use by 
		 * SEC_ASN1GetSubtemplate (specifically, by dynamic
		 * choosers)
		 */
		sec_asn1d_state *tempState = state;
		while(subDest == NULL) {
			sec_asn1d_state *parent = tempState->parent;
			if(parent == NULL) {
				/* Oh well. Not going to work for this template. */
				break;
			}
			subDest = parent->dest;
			tempState = parent;
		}
		#endif	/* __APPLE__ */
		subt = SEC_ASN1GetSubtemplate (state->theTemplate, subDest,
			PR_FALSE, buf /* __APPLE__ */);
		state = sec_asn1d_push_state (state->top, subt, dest, PR_FALSE);
		if (state == NULL)
			return NULL;
	
		state->allocate = child_allocate;
	
		if (universal
			#ifdef	__APPLE__
			/* Dynamic: restart with new template */
			|| dynamic
			#endif
			) {
			state = sec_asn1d_init_state_based_on_template (state,
				buf /* __APPLE__ */);
			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_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 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
	if (doDumpStates > 0) {
        char kindBuf[256];
        formatKind(byte, kindBuf);
        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;
		dprintf("decodeError: parse_more_id high bits oflow\n");
	    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;
			//dprintf("decodeError: sec_asn1d_confirm_identifier\n");
		}
    }
}


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;
			dprintf("decodeError: sec_asn1d_parse_more_length\n");
			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;
}


static void
sec_asn1d_prepare_for_contents (sec_asn1d_state *state,
	#ifdef	__APPLE__
	const char *buf		/* needed for SEC_ASN1GetSubtemplate */
	#endif
	)
{
    SecAsn1Item *item;
    PRArenaPool *poolp;
    unsigned long alloc_len;

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

    /*
     * 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) {
			dprintf("decodeError: sec_asn1d_prepare_for_contents zalloc\n");
			state->top->status = decodeError;
			return;
		}
		/* FIXME _ we're losing the dest pointer after this! */
		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;

    /* If this item has definite length encoding, and 
    ** is enclosed by a definite length constructed type,
    ** make sure it isn't longer than the remaining space in that 
    ** constructed type.  
    */
    if (state->contents_length > 0) {
        sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(state);
        if (parent && !parent->indefinite && 
            state->consumed + state->contents_length > parent->pending) {
            PORT_SetError (SEC_ERROR_BAD_DER);
            state->top->status = decodeError;
            return;
        }
    }
    
    /*
     * 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,
								 buf /* __APPLE__ */),
				      state->dest, PR_TRUE);
		if (state != NULL)
			state = sec_asn1d_init_state_based_on_template (state,
				buf /* __APPLE__ */);
		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_SEQUENCE_OF|SEC_ASN1_DYNAMIC)
	   || state->underlying_kind == (SEC_ASN1_SET_OF|SEC_ASN1_DYNAMIC)
		     );
	if (state->contents_length != 0 || state->indefinite) {
	    const SecAsn1Template *subt;

	    state->place = duringGroup;
	    subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->dest,
					   PR_FALSE, buf /* __APPLE__ */);
	    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);
			state = sec_asn1d_init_state_based_on_template (state,
				buf /* __APPLE__ */);
	    }
	} 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);
	    state = sec_asn1d_init_state_based_on_template (state,
			buf /* __APPLE__ */);
	}
	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.
	 */
	dprintf("decodeError: prepare for contents SEC_ASN1_SET\n");
	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) {
	    dprintf("decodeError: prepare for contents indefinite NULL\n");
	    PORT_SetError (SEC_ERROR_BAD_DER);
	    state->top->status = decodeError;
	    break;
	}
	if (state->dest != NULL) {
	    item = (SecAsn1Item *)(state->dest);
	    item->Data = NULL;
	    item->Length = 0;
	}
	state->place = afterEndOfContents;
	break;

      case SEC_ASN1_BMP_STRING:
	/* Error if length is not divisable by 2 */
	if (state->contents_length % 2) {
	   dprintf("decodeError: prepare for contents odd length BMP_STRING\n");
	   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) {
	   dprintf("decodeError: prepare for contents odd length UNIV_STRING\n");
	   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 = (SecAsn1Item *)(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->Length = 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->Length == 0);
			poolp = state->top->our_pool;
	    } else {
			alloc_len = 0;
	    }
	} else {
	    item->Length = 0;
	    item->Data = NULL;
	    poolp = state->top->their_pool;
	}

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

	    PORT_Assert (item->Length == 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;
	    }

	    item->Data = (unsigned char*)sec_asn1d_zalloc (poolp, alloc_len);
	    if (item->Data == NULL) {
		    dprintf("decodeError: prepare for contents zalloc\n");
			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->Length = 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 SecAsn1Template *sub;

	    switch (state->underlying_kind) {
	      case SEC_ASN1_ANY:
	      case SEC_ASN1_ANY_CONTENTS:
		sub = kSecAsn1AnyTemplate;
		break;
	      case SEC_ASN1_BIT_STRING:
		sub = kSecAsn1BitStringTemplate;
		break;
	      case SEC_ASN1_BMP_STRING:
		sub = kSecAsn1BMPStringTemplate;
		break;
	      case SEC_ASN1_GENERALIZED_TIME:
		sub = kSecAsn1GeneralizedTimeTemplate;
		break;
	      case SEC_ASN1_IA5_STRING:
		sub = kSecAsn1IA5StringTemplate;
		break;
	      case SEC_ASN1_OCTET_STRING:
		sub = kSecAsn1OctetStringTemplate;
		break;
	      case SEC_ASN1_PRINTABLE_STRING:
		sub = kSecAsn1PrintableStringTemplate;
		break;
	      case SEC_ASN1_T61_STRING:
		sub = kSecAsn1T61StringTemplate;
		break;
	      case SEC_ASN1_UNIVERSAL_STRING:
		sub = kSecAsn1UniversalStringTemplate;
		break;
	      case SEC_ASN1_UTC_TIME:
		sub = kSecAsn1UTCTimeTemplate;
		break;
	      case SEC_ASN1_UTF8_STRING:
		sub = kSecAsn1UTF8StringTemplate;
		break;
	      case SEC_ASN1_VISIBLE_STRING:
		sub = kSecAsn1VisibleStringTemplate;
		break;
	      case SEC_ASN1_SKIP:
		sub = kSecAsn1SkipTemplate;
		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? */
		state = sec_asn1d_init_state_based_on_template (state,
			buf /* __APPLE__ */);
	    }
	} else if (state->indefinite) {
	    /*
	     * An indefinite-length string *must* be constructed!
	     */
	    dprintf("decodeError: prepare for contents indefinite not construncted\n");
	    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 = (SecAsn1Item *)(state->dest);
			item->Length = 0;
			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) {
					dprintf("decodeError: prepare for contents zalloc\n");
					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.)
	     */
		dprintf("decodeError: prepare for contents indefinite zero len \n");
	    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_ArenaRelease (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;
    SecAsn1Item *item;
    void *dest;


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

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

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

    PORT_Assert (item->Length == 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,
		(char *) item->Data /* __APPLE__ */);

    /*
     * Now parse that out of our data.
     */
    if (SEC_ASN1DecoderUpdate (state->top,
			       (char *) item->Data, item->Length) != 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)
{
    SecAsn1Item *item;
    unsigned long bufLen;

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

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

    bufLen = len;

    item = (SecAsn1Item *)(state->dest);
    if (item != NULL && item->Data != NULL) {
		/* Strip leading zeroes when target is unsigned integer */
		if (state->underlying_kind == SEC_ASN1_INTEGER && /* INTEGER   */
			item->Length == 0 &&                          /* MSB       */
			#ifdef	__APPLE__
			!(state->underlying_kind & SEC_ASN1_SIGNED_INT))
			#else
			item->type == siUnsignedInteger)              /* unsigned  */
			#endif
		{
			while (len > 1 && buf[0] == 0) {              /* leading 0 */
				buf++;
				len--;
			}
		}
		PORT_Memcpy (item->Data + item->Length, buf, len);
		item->Length += 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) || (state->contents_length == 1)) {
		if (state->dest != NULL) {
			SecAsn1Item *item = (SecAsn1Item *)(state->dest);
			item->Data = NULL;
			item->Length = 0;
			state->place = beforeEndOfContents;
		}
		if(state->contents_length == 1) {
			/* skip over (unused) remainder byte */
			return 1;
		}
		else {
			return 0;
		}
    }

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

    byte = (unsigned char) *buf;
    if (byte > 7) {
		dprintf("decodeError: parse_bit_string remainder oflow\n");
		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);
    if (state->place == beforeEndOfContents && state->dest != NULL) {
	SecAsn1Item *item;

	item = (SecAsn1Item *)(state->dest);
	if (item->Length)
	    item->Length = (item->Length << 3) - state->bit_string_unused_bits;
    }

    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) {
		dprintf("decodeError: zalloc\n");	
		state->top->status = decodeError;
		return NULL;
    }

    if (copy_data) {
	void *copy;
	copy = sec_asn1d_alloc (state->top->our_pool, len);
	if (copy == NULL) {
		dprintf("decodeError: alloc\n");	
	    state->top->status = decodeError;
	    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)
{
    SecAsn1Item *item;

    item = (SecAsn1Item *)(state->dest);
    if (item != NULL && item->Data != NULL) {
	PORT_Assert (state->substring);
	PORT_Memcpy (item->Data + item->Length, buf, len);
	item->Length += 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;
    SecAsn1Item *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 ) {
		dprintf("decodeError: next_substring consumed > pend\n");	
	    PORT_SetError (SEC_ERROR_BAD_DER);
	    state->top->status = decodeError;
	    return;
	}

	state->pending -= child_consumed;
	if (state->pending == 0)
	    done = PR_TRUE;
    } else {
	PORT_Assert (state->indefinite);

	item = (SecAsn1Item *)(child->dest);
	if (item != NULL && item->Data != NULL) {
	    /*
	     * Save the string away for later concatenation.
	     */
	    PORT_Assert (item->Data != NULL);
	    sec_asn1d_add_to_subitems (state, item->Data, item->Length, PR_FALSE);
	    /*
	     * Clear the child item for the next round.
	     */
	    item->Data = NULL;
	    item->Length = 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,
	const char *buf		/* __APPLE__ */)
{
    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.
     */
	#ifdef	__APPLE__
	/* 
	 * Without the check for !child->indefinite, this path could
	 * be taken erroneously if the child is indefinite!
	 */
	if(child->endofcontents && !child->indefinite) {
	#else
    if (child->endofcontents) {
	#endif	/* __APPLE__ */
	/* 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.
	 */
	PORT_Assert (state->indefinite);
	PORT_Assert (state->pending == 0);
	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 ) {
		dprintf("decodeError: next_in_group consumed > pend\n");	
	    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, buf /* __APPLE__ */);

    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,
	const char *buf /* __APPLE__ */)
{
    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 ) {
				dprintf("decodeError: next_in_seq consumed > pend\n");	
				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) {
					dprintf("decodeError: next_in_seq child not opt\n");	
					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) {
		dprintf("decodeError: next_in_seq notInUse still pending\n");	
	    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 ) {
			dprintf("decodeError: next_in_seq indef len != 2\n");	
		    PORT_SetError (SEC_ERROR_BAD_DER);
		    state->top->status = decodeError;
		} else {
		    state->consumed += child_consumed;
		    state->place = afterEndOfContents;
		}
	    } else {
			dprintf("decodeError: next_in_seq !indef, child missing\n");	
			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, 
		buf /* __APPLE__ */);
	if (child_missing) {
	    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.
		 */
		PORT_Assert (child_found_tag_number < SEC_ASN1_HIGH_TAG_NUMBER);
		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;
	SecAsn1Item *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)) {
			dprintf("decodeError: sec_asn1d_concat_substrings align\n");	
			PORT_SetError (SEC_ERROR_BAD_DER);
			state->top->status = decodeError;
			return;
	    }
	    item_len += substring->len;
	    substring = substring->next;
	}

	if (is_bit_string) {
#ifdef XP_WIN16		/* win16 compiler gets an internal error otherwise */
	    alloc_len = (((long)item_len + 7) / 8);
#else
	    alloc_len = ((item_len + 7) >> 3);
#endif
	} 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;
	}

	item = (SecAsn1Item *)(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) {
		dprintf("decodeError: zalloc\n");	
	    state->top->status = decodeError;
	    return;
	}
	item->Length = 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) {
		dprintf("decodeError: zalloc\n");	
	    state->top->status = decodeError;
	    return;
	}

	*placep = group;

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

	/*
	 * Because we use arenas and have a mark set, we later free
	 * everything we have allocated, so this does *not* present
	 * a memory leak (it is just temporarily left dangling).
	 */
	state->subitems_head = state->subitems_tail = NULL;
    }

    state->place = afterEndOfContents;
}

/*
 * For those states that push a child to handle a subtemplate,
 * "absorb" that child (transfer necessary information).
 */
static void
sec_asn1d_absorb_child (sec_asn1d_state *state)
{
    /*
     * There is absolutely supposed to be a child there.
     */
    PORT_Assert (state->child != NULL);

    /*
     * Inherit the missing status of our child, and do the ugly
     * backing-up if necessary.
     */
    state->missing = state->child->missing;
    if (state->missing) {
	state->found_tag_number = state->child->found_tag_number;
	state->found_tag_modifiers = state->child->found_tag_modifiers;
	state->endofcontents = state->child->endofcontents;
    }

    /*
     * Add in number of bytes consumed by child.
     * (Only EXPLICIT should have already consumed bytes itself.)
     */
    PORT_Assert (state->place == afterExplicit || state->consumed == 0);
    state->consumed += state->child->consumed;

    /*
     * Subtract from bytes pending; this only applies to a definite-length
     * EXPLICIT field.
     */
    if (state->pending) {
	PORT_Assert (!state->indefinite);
	PORT_Assert (state->place == afterExplicit);

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

    /*
     * Indicate that we are done with child.
     */
    state->child->consumed = 0;

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


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

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


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

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

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

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

    for (i = 0; i < len; i++) {
	if (buf[i] != 0) {
	    /*
	     * We expect to find only zeros; if not, just give up.
	     */
		dprintf("decodeError: end of contents non zero\n");	
	    PORT_SetError (SEC_ERROR_BAD_DER);
	    state->top->status = decodeError;
	    return 0;
	}
    }

    state->pending -= len;

    if (state->pending == 0) {
	state->place = afterEndOfContents;
	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 ) {
			dprintf("decodeError: pop_state pending < consumed\n");	
			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, const char *buf /* __APPLE__ */)
{
	sec_asn1d_state *child;

	if( state->allocate ) {
		void *dest;
	
		dest = sec_asn1d_zalloc(state->top->their_pool, 
			state->theTemplate->size);
		if( (void *)NULL == dest ) {
			dprintf("decodeError: zalloc\n");	
			state->top->status = decodeError;
			return (sec_asn1d_state *)NULL;
		}
	
		state->dest = (char *)dest + state->theTemplate->offset;
	}

	child = sec_asn1d_push_state(state->top, state->theTemplate + 1, 
                                     (char *)state->dest - state->theTemplate->offset, 
                                     PR_FALSE);
	if( (sec_asn1d_state *)NULL == child ) {
		return (sec_asn1d_state *)NULL;
	}
	
	sec_asn1d_scrub_state(child);
	child = sec_asn1d_init_state_based_on_template(child, 
		buf /* __APPLE__ */);
	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, const char *buf /* __APPLE__ */)
{
  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;
        dprintf("decodeError: during_choice child at EOC by parent does not allow EOC\n");	
        PORT_SetError(SEC_ERROR_BAD_DER);
        state->top->status = decodeError;
        return NULL;
    }

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

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

    /* cargo'd from next_in_sequence innards */
    if( state->pending ) {
      PORT_Assert(!state->indefinite);
      if( child->consumed > state->pending ) {
			dprintf("decodeError: during_choice consumed > pending\n");	
			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);
		dprintf("decodeError: during_choice !pending\n");	
        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, buf /* __APPLE__*/);
    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);
}

#if 0
unsigned long
sec_asn1d_uinteger(SecAsn1Item *src)
{
    unsigned long value;
    int len;

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

    value = 0;
    len = src->Length;
    while (len) {
	value <<= 8;
	value |= src->Data[--len];
    }
    return value;
}
#endif

SECStatus
SEC_ASN1DecodeInteger(SecAsn1Item *src, unsigned long *value)
{
    unsigned long v;
    unsigned int i;
    
    if (src == NULL) {
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

    if (src->Length > 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->Length; 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;
    char kindBuf[256];
    
    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);
        printf("%s: tmpl %p, kind%s",
               (state == cx->current) ? "STATE" : "State",
               state->theTemplate,
               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, size_t len)
{
    sec_asn1d_state *state = NULL;
    unsigned long consumed;
    SEC_ASN1EncodingPart what;
    sec_asn1d_state *stateEnd = cx->current;

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

    while (cx->status == keepGoing) {
		state = cx->current;
		what = SEC_ASN1_Contents;
		consumed = 0;
		#if DEBUG_ASN1D_STATES
		if (doDumpStates > 1) {
                    printf("\nPLACE = %s, next byte = 0x%02x, %p[%lu]\n",
                           (state->place <= notInUse) ?
                           place_names[ state->place ] : "(undefined)",
                           (unsigned int)((unsigned char *)buf)[ consumed ],
                           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, buf);
			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, buf);
			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, buf);
			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, buf);
				break;
			case duringChoice:
				state = sec_asn1d_during_choice(state, buf);
				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);
			dprintf("decodeError: decoder update bad state->place\n");	
			cx->status = decodeError;
			break;
		}
	
		if (cx->status == decodeError)
			break;
	
		/* We should not consume more than we have.  */
		PORT_Assert (consumed <= len);
		if( consumed > len ) {
			dprintf("decodeError: decoder update consumed > len\n");	
			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) {
				dprintf("decodeError: decoder update nonzero len\n");	
				PORT_SetError (SEC_ERROR_BAD_DER);
				cx->status = decodeError;
			}
			else
	#endif
			cx->status = allDone;
			break;
		}
		else if (state->theTemplate->kind == SEC_ASN1_SKIP_REST) {
			cx->status = allDone;
			break;
		}
		
		if (consumed == 0)
			continue;
	
		/*
		 * The following check is specifically looking for an ANY
		 * that is *not* also an INNER, because we need to save aside
		 * all bytes in that case -- the contents parts will get
		 * handled like all other contents, and the end-of-contents
		 * bytes are added by the concat code, but the outer header
		 * bytes need to get saved too, so we do them explicitly here.
		 */
		if (state->underlying_kind == SEC_ASN1_ANY
			&& !cx->filter_only && (what == SEC_ASN1_Identifier
						|| what == SEC_ASN1_Length)) {
			sec_asn1d_record_any_header (state, buf, consumed);
		}
	
		/*
		 * We had some number of good, accepted bytes.  If the caller
		 * has registered to see them, pass them along.
		 */
		if (state->top->filter_proc != NULL) {
			int depth;
	
			depth = state->depth;
			if (what == SEC_ASN1_EndOfContents && !state->indefinite) {
				PORT_Assert (state->parent != NULL
						&& state->parent->indefinite);
				depth--;
				PORT_Assert (depth == state->parent->depth);
			}
			(* state->top->filter_proc) (state->top->filter_arg,
						buf, consumed, depth, what);
		}
	
		state->consumed += consumed;
		buf += consumed;
		len -= consumed;
    }	/* main decode loop */

    if (cx->status == decodeError) {
		while (state != NULL && stateEnd->parent!=state) {
			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);
		}
#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->status == needBytes) {
		#ifdef	__APPLE__
		/*
		 * Special case: need more bytes, but this field and all
		 * subsequent fields are optional. I'm surprised this case is
		 * not handled in the original NSS code, and this workaround
		 * is a bit of a hack...
		 */
		sec_asn1d_state *state = cx->current;
		assert(state != NULL);
		if(state->place == beforeIdentifier) {
			int allOptional = 1;
			const SecAsn1Template *templ = state->theTemplate;
			while(templ->kind != 0) {
				if(!(templ->kind & SEC_ASN1_OPTIONAL)) {
					allOptional = 0;
					break;
				}
				templ++;
			}
			if(allOptional) {
				/* letting this one slide */
				rv = SECSuccess;
			}
			else {
				PORT_SetError (SEC_ERROR_BAD_DER);
				rv = SECFailure;
			}
		}
		else {
			PORT_SetError (SEC_ERROR_BAD_DER);
			rv = SECFailure;
		}
		#else
		PORT_SetError (SEC_ERROR_BAD_DER);
		rv = SECFailure;
		#endif	/* __APPLE__ */
    } else {
		rv = SECSuccess;
    }

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

    PORT_FreeArena (cx->our_pool, PR_FALSE);

    return rv;
}


SEC_ASN1DecoderContext *
SEC_ASN1DecoderStart (PRArenaPool *their_pool, void *dest,
		      const SecAsn1Template *theTemplate
			  #ifdef	__APPLE__
			  ,
			  /* only needed if first element will be SEC_ASN1_DYNAMIC */
			  const char *buf
			  #endif	
			  )
{
    PRArenaPool *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, 
			buf /* __APPLE__ */) == 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_ASN1DecoderAbort(SEC_ASN1DecoderContext *cx, int error)
{
    PORT_Assert(cx);
    PORT_SetError(error);
    cx->status = decodeError;
}


SECStatus
SEC_ASN1Decode (PRArenaPool *poolp, void *dest,
		const SecAsn1Template *theTemplate,
		const char *buf, size_t len)
{
    SEC_ASN1DecoderContext *dcx;
    SECStatus urv, frv;

    dcx = SEC_ASN1DecoderStart (poolp, dest, theTemplate,
		buf /* __APPLE__ */);
    if (dcx == NULL)
	return SECFailure;

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

    if (urv != SECSuccess)
	return urv;

    return frv;
}


SECStatus
SEC_ASN1DecodeItem (PRArenaPool *poolp, void *dest,
		    const SecAsn1Template *theTemplate,
		    const SecAsn1Item *item)
{
    return SEC_ASN1Decode (poolp, dest, theTemplate,
			   (const char *) item->Data, item->Length);
}