]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_asn1/lib/secasn1d.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_asn1 / lib / secasn1d.c
diff --git a/Security/libsecurity_asn1/lib/secasn1d.c b/Security/libsecurity_asn1/lib/secasn1d.c
new file mode 100644 (file)
index 0000000..c3971d0
--- /dev/null
@@ -0,0 +1,3167 @@
+/*
+ * 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 || state->theTemplate == NULL)
+               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=NULL;
+    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__ */);
+        (void) 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_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;
+       }
+    (void) state;
+       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__ */);
+       }
+    (void) 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.
+        */
+       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!=NULL);
+        if (item==NULL) {
+            PORT_SetError (SEC_ERROR_BAD_DER);
+            state->top->status = decodeError;
+            return;
+        }
+        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;
+       }
+    (void) state;
+       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;
+        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)
+{
+    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) {
+           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);
+}
+
+