tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

secasn1e.c (51533B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * Support for ENcoding ASN.1 data based on BER/DER (Basic/Distinguished
      7 * Encoding Rules).
      8 */
      9 
     10 #include "secasn1.h"
     11 
     12 typedef enum {
     13    beforeHeader,
     14    duringContents,
     15    duringGroup,
     16    duringSequence,
     17    afterContents,
     18    afterImplicit,
     19    afterInline,
     20    afterPointer,
     21    afterChoice,
     22    notInUse
     23 } sec_asn1e_parse_place;
     24 
     25 typedef enum {
     26    allDone,
     27    encodeError,
     28    keepGoing,
     29    needBytes
     30 } sec_asn1e_parse_status;
     31 
     32 typedef enum {
     33    hdr_normal = 0,     /* encode header normally */
     34    hdr_any = 1,        /* header already encoded in content */
     35    hdr_decoder = 2,    /* template only used by decoder. skip it. */
     36    hdr_optional = 3,   /* optional component, to be omitted */
     37    hdr_placeholder = 4 /* place holder for from_buf content */
     38 } sec_asn1e_hdr_encoding;
     39 
     40 typedef struct sec_asn1e_state_struct {
     41    SEC_ASN1EncoderContext *top;
     42    const SEC_ASN1Template *theTemplate;
     43    void *src;
     44 
     45    struct sec_asn1e_state_struct *parent; /* aka prev */
     46    struct sec_asn1e_state_struct *child;  /* aka next */
     47 
     48    sec_asn1e_parse_place place; /* where we are in encoding process */
     49 
     50    /*
     51     * XXX explain the next fields as clearly as possible...
     52     */
     53    unsigned char tag_modifiers;
     54    unsigned char tag_number;
     55    unsigned long underlying_kind;
     56 
     57    int depth;
     58 
     59    PRBool isExplicit,     /* we are handling an isExplicit header */
     60        indefinite,        /* need end-of-contents */
     61        is_string,         /* encoding a simple string or an ANY */
     62        may_stream,        /* when streaming, do indefinite encoding */
     63        optional,          /* omit field if it has no contents */
     64        disallowStreaming; /* disallow streaming in all sub-templates */
     65 } sec_asn1e_state;
     66 
     67 /*
     68 * An "outsider" will have an opaque pointer to this, created by calling
     69 * SEC_ASN1EncoderStart().  It will be passed back in to all subsequent
     70 * calls to SEC_ASN1EncoderUpdate() and related routines, and when done
     71 * it is passed to SEC_ASN1EncoderFinish().
     72 */
     73 struct sec_EncoderContext_struct {
     74    PLArenaPool *our_pool; /* for our internal allocs */
     75 
     76    sec_asn1e_state *current;
     77    sec_asn1e_parse_status status;
     78 
     79    PRBool streaming;
     80    PRBool from_buf;
     81 
     82    SEC_ASN1NotifyProc notify_proc; /* call before/after handling field */
     83    void *notify_arg;               /* argument to notify_proc */
     84    PRBool during_notify;           /* true during call to notify_proc */
     85 
     86    SEC_ASN1WriteProc output_proc; /* pass encoded bytes to this  */
     87    void *output_arg;              /* argument to that function */
     88 };
     89 
     90 static sec_asn1e_state *
     91 sec_asn1e_push_state(SEC_ASN1EncoderContext *cx,
     92                     const SEC_ASN1Template *theTemplate,
     93                     const void *src, PRBool new_depth)
     94 {
     95    sec_asn1e_state *state, *new_state;
     96 
     97    if (theTemplate == NULL) {
     98        cx->status = encodeError;
     99        return NULL;
    100    }
    101 
    102    state = cx->current;
    103    new_state = (sec_asn1e_state *)PORT_ArenaZAlloc(cx->our_pool,
    104                                                    sizeof(*new_state));
    105    if (new_state == NULL) {
    106        cx->status = encodeError;
    107        return NULL;
    108    }
    109 
    110    new_state->top = cx;
    111    new_state->parent = state;
    112    new_state->theTemplate = theTemplate;
    113    new_state->place = notInUse;
    114    if (src != NULL)
    115        new_state->src = (char *)src + theTemplate->offset;
    116 
    117    if (state != NULL) {
    118        new_state->depth = state->depth;
    119        if (new_depth)
    120            new_state->depth++;
    121        state->child = new_state;
    122    }
    123 
    124    cx->current = new_state;
    125    return new_state;
    126 }
    127 
    128 static void
    129 sec_asn1e_scrub_state(sec_asn1e_state *state)
    130 {
    131    /*
    132     * Some default "scrubbing".
    133     * XXX right set of initializations?
    134     */
    135    state->place = beforeHeader;
    136    state->indefinite = PR_FALSE;
    137 }
    138 
    139 static void
    140 sec_asn1e_notify_before(SEC_ASN1EncoderContext *cx, void *src, int depth)
    141 {
    142    if (cx->notify_proc == NULL)
    143        return;
    144 
    145    cx->during_notify = PR_TRUE;
    146    (*cx->notify_proc)(cx->notify_arg, PR_TRUE, src, depth);
    147    cx->during_notify = PR_FALSE;
    148 }
    149 
    150 static void
    151 sec_asn1e_notify_after(SEC_ASN1EncoderContext *cx, void *src, int depth)
    152 {
    153    if (cx->notify_proc == NULL)
    154        return;
    155 
    156    cx->during_notify = PR_TRUE;
    157    (*cx->notify_proc)(cx->notify_arg, PR_FALSE, src, depth);
    158    cx->during_notify = PR_FALSE;
    159 }
    160 
    161 static sec_asn1e_state *
    162 sec_asn1e_init_state_based_on_template(sec_asn1e_state *state)
    163 {
    164    PRBool isExplicit, is_string, may_stream, optional, universal;
    165    PRBool disallowStreaming;
    166    unsigned char tag_modifiers;
    167    unsigned long encode_kind, under_kind;
    168    unsigned long tag_number;
    169    PRBool isInline = PR_FALSE;
    170 
    171    encode_kind = state->theTemplate->kind;
    172 
    173    universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
    174                    ? PR_TRUE
    175                    : PR_FALSE;
    176 
    177    isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
    178    encode_kind &= ~SEC_ASN1_EXPLICIT;
    179 
    180    optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
    181    encode_kind &= ~SEC_ASN1_OPTIONAL;
    182 
    183    PORT_Assert(!(isExplicit && universal)); /* bad templates */
    184 
    185    may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
    186    encode_kind &= ~SEC_ASN1_MAY_STREAM;
    187 
    188    disallowStreaming = (encode_kind & SEC_ASN1_NO_STREAM) ? PR_TRUE : PR_FALSE;
    189    encode_kind &= ~SEC_ASN1_NO_STREAM;
    190 
    191    /* Just clear this to get it out of the way; we do not need it here */
    192    encode_kind &= ~SEC_ASN1_DYNAMIC;
    193 
    194    if (encode_kind & SEC_ASN1_CHOICE) {
    195        under_kind = SEC_ASN1_CHOICE;
    196    } else if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) ||
    197               (!universal && !isExplicit)) {
    198        const SEC_ASN1Template *subt;
    199        void *src = NULL;
    200 
    201        PORT_Assert((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0);
    202 
    203        sec_asn1e_scrub_state(state);
    204 
    205        if (encode_kind & SEC_ASN1_POINTER) {
    206            src = *(void **)state->src;
    207            state->place = afterPointer;
    208 
    209            if (src == NULL) {
    210                /*
    211                 * If this is optional, but NULL, then the field does
    212                 * not need to be encoded.  In this case we are done;
    213                 * we do not want to push a subtemplate.
    214                 */
    215                if (optional)
    216                    return state;
    217 
    218                /*
    219                 * XXX this is an error; need to figure out
    220                 * how to handle this
    221                 */
    222            }
    223        } else {
    224            src = state->src;
    225            if (encode_kind & SEC_ASN1_INLINE) {
    226                /* check that there are no extraneous bits */
    227                /* PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional); */
    228                state->place = afterInline;
    229                isInline = PR_TRUE;
    230            } else {
    231                /*
    232                 * Save the tag modifiers and tag number here before moving
    233                 * on to the next state in case this is a member of a
    234                 * SEQUENCE OF
    235                 */
    236                state->tag_modifiers = (unsigned char)(encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK));
    237                state->tag_number = (unsigned char)(encode_kind & SEC_ASN1_TAGNUM_MASK);
    238 
    239                state->place = afterImplicit;
    240                state->optional = optional;
    241            }
    242        }
    243 
    244        subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->src, PR_TRUE);
    245        if (isInline && optional) {
    246            /* we only handle a very limited set of optional inline cases at
    247               this time */
    248            if (PR_FALSE != SEC_ASN1IsTemplateSimple(subt)) {
    249                /* we now know that the target is a SECItem*, so we can check
    250                   if the source contains one */
    251                SECItem *target = (SECItem *)state->src;
    252                if (!target || !target->data || !target->len) {
    253                    /* no valid data to encode subtemplate */
    254                    return state;
    255                }
    256            } else {
    257                PORT_Assert(0); /* complex templates are not handled as
    258                                   inline optional */
    259            }
    260        }
    261        state = sec_asn1e_push_state(state->top, subt, src, PR_FALSE);
    262        if (state == NULL)
    263            return state;
    264 
    265        if (universal) {
    266            /*
    267             * This is a POINTER or INLINE; just init based on that
    268             * and we are done.
    269             */
    270            return sec_asn1e_init_state_based_on_template(state);
    271        }
    272 
    273        /*
    274         * This is an implicit, non-universal (meaning, application-private
    275         * or context-specific) field.  This results in a "magic" tag but
    276         * encoding based on the underlying type.  We pushed a new state
    277         * that is based on the subtemplate (the underlying type), but
    278         * now we will sort of alias it to give it some of our properties
    279         * (tag, optional status, etc.).
    280         *
    281         * NB: ALL the following flags in the subtemplate are disallowed
    282         *     and/or ignored: EXPLICIT, OPTIONAL, INNER, INLINE, POINTER.
    283         */
    284 
    285        under_kind = state->theTemplate->kind;
    286        if ((under_kind & SEC_ASN1_MAY_STREAM) && !disallowStreaming) {
    287            may_stream = PR_TRUE;
    288        }
    289        under_kind &= ~(SEC_ASN1_MAY_STREAM | SEC_ASN1_DYNAMIC);
    290    } else {
    291        under_kind = encode_kind;
    292    }
    293 
    294 /*
    295 * Sanity check that there are no unwanted bits marked in under_kind.
    296 * These bits were either removed above (after we recorded them) or
    297 * they simply should not be found (signalling a bad/broken template).
    298 * XXX is this the right set of bits to test here? (i.e. need to add
    299 * or remove any?)
    300 */
    301 #define UNEXPECTED_FLAGS                                                      \
    302    (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_SKIP | SEC_ASN1_INNER | \
    303     SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)
    304 
    305    PORT_Assert((under_kind & UNEXPECTED_FLAGS) == 0);
    306    under_kind &= ~UNEXPECTED_FLAGS;
    307 #undef UNEXPECTED_FLAGS
    308 
    309    if (encode_kind & SEC_ASN1_ANY) {
    310        PORT_Assert(encode_kind == under_kind);
    311        tag_modifiers = 0;
    312        tag_number = 0;
    313        is_string = PR_TRUE;
    314    } else {
    315        tag_modifiers = (unsigned char)(encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK));
    316        /*
    317         * XXX This assumes only single-octet identifiers.  To handle
    318         * the HIGH TAG form we would need to do some more work, especially
    319         * in how to specify them in the template, because right now we
    320         * do not provide a way to specify more *tag* bits in encode_kind.
    321         */
    322        tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;
    323 
    324        is_string = PR_FALSE;
    325        switch (under_kind & SEC_ASN1_TAGNUM_MASK) {
    326            case SEC_ASN1_SET:
    327                /*
    328                 * XXX A plain old SET (as opposed to a SET OF) is not implemented.
    329                 * If it ever is, remove this assert...
    330                 */
    331                PORT_Assert((under_kind & SEC_ASN1_GROUP) != 0);
    332            /* fallthru */
    333            case SEC_ASN1_SEQUENCE:
    334                tag_modifiers |= SEC_ASN1_CONSTRUCTED;
    335                break;
    336            case SEC_ASN1_BIT_STRING:
    337            case SEC_ASN1_BMP_STRING:
    338            case SEC_ASN1_GENERALIZED_TIME:
    339            case SEC_ASN1_IA5_STRING:
    340            case SEC_ASN1_OCTET_STRING:
    341            case SEC_ASN1_PRINTABLE_STRING:
    342            case SEC_ASN1_T61_STRING:
    343            case SEC_ASN1_UNIVERSAL_STRING:
    344            case SEC_ASN1_UTC_TIME:
    345            case SEC_ASN1_UTF8_STRING:
    346            case SEC_ASN1_VISIBLE_STRING:
    347                /*
    348                 * We do not yet know if we will be constructing the string,
    349                 * so we have to wait to do this final tag modification.
    350                 */
    351                is_string = PR_TRUE;
    352                break;
    353        }
    354    }
    355 
    356    state->tag_modifiers = tag_modifiers;
    357    state->tag_number = (unsigned char)tag_number;
    358    state->underlying_kind = under_kind;
    359    state->isExplicit = isExplicit;
    360    state->may_stream = may_stream;
    361    state->is_string = is_string;
    362    state->optional = optional;
    363    state->disallowStreaming = disallowStreaming;
    364 
    365    sec_asn1e_scrub_state(state);
    366 
    367    return state;
    368 }
    369 
    370 static void
    371 sec_asn1e_write_part(sec_asn1e_state *state,
    372                     const char *buf, unsigned long len,
    373                     SEC_ASN1EncodingPart part)
    374 {
    375    SEC_ASN1EncoderContext *cx;
    376 
    377    cx = state->top;
    378    (*cx->output_proc)(cx->output_arg, buf, len, state->depth, part);
    379 }
    380 
    381 /*
    382 * XXX This assumes only single-octet identifiers.  To handle
    383 * the HIGH TAG form we would need to modify this interface and
    384 * teach it to properly encode the special form.
    385 */
    386 static void
    387 sec_asn1e_write_identifier_bytes(sec_asn1e_state *state, unsigned char value)
    388 {
    389    char byte;
    390 
    391    byte = (char)value;
    392    sec_asn1e_write_part(state, &byte, 1, SEC_ASN1_Identifier);
    393 }
    394 
    395 int
    396 SEC_ASN1EncodeLength(unsigned char *buf, int value)
    397 {
    398    int lenlen;
    399 
    400    lenlen = SEC_ASN1LengthLength(value);
    401    if (lenlen == 1) {
    402        buf[0] = value;
    403    } else {
    404        int i;
    405 
    406        i = lenlen - 1;
    407        buf[0] = 0x80 | i;
    408        while (i) {
    409            buf[i--] = value;
    410            value >>= 8;
    411        }
    412        PORT_Assert(value == 0);
    413    }
    414    return lenlen;
    415 }
    416 
    417 static void
    418 sec_asn1e_write_length_bytes(sec_asn1e_state *state, unsigned long value,
    419                             PRBool indefinite)
    420 {
    421    int lenlen;
    422    unsigned char buf[sizeof(unsigned long) + 1];
    423 
    424    if (indefinite) {
    425        PORT_Assert(value == 0);
    426        buf[0] = 0x80;
    427        lenlen = 1;
    428    } else {
    429        lenlen = SEC_ASN1EncodeLength(buf, value);
    430    }
    431 
    432    sec_asn1e_write_part(state, (char *)buf, lenlen, SEC_ASN1_Length);
    433 }
    434 
    435 static void
    436 sec_asn1e_write_contents_bytes(sec_asn1e_state *state,
    437                               const char *buf, unsigned long len)
    438 {
    439    sec_asn1e_write_part(state, buf, len, SEC_ASN1_Contents);
    440 }
    441 
    442 static void
    443 sec_asn1e_write_end_of_contents_bytes(sec_asn1e_state *state)
    444 {
    445    const char eoc[2] = { 0, 0 };
    446 
    447    sec_asn1e_write_part(state, eoc, 2, SEC_ASN1_EndOfContents);
    448 }
    449 
    450 static int
    451 sec_asn1e_which_choice(
    452    void *src,
    453    const SEC_ASN1Template *theTemplate)
    454 {
    455    int rv;
    456    unsigned int which = *(unsigned int *)src;
    457 
    458    for (rv = 1, theTemplate++; theTemplate->kind != 0; rv++, theTemplate++) {
    459        if (which == theTemplate->size) {
    460            return rv;
    461        }
    462    }
    463 
    464    return 0;
    465 }
    466 
    467 static unsigned long
    468 sec_asn1e_contents_length(const SEC_ASN1Template *theTemplate, void *src,
    469                          PRBool disallowStreaming, PRBool insideIndefinite,
    470                          sec_asn1e_hdr_encoding *pHdrException)
    471 {
    472    unsigned long encode_kind, underlying_kind;
    473    PRBool isExplicit, optional, universal, may_stream;
    474    unsigned long len;
    475 
    476    /*
    477     * This function currently calculates the length in all cases
    478     * except the following: when writing out the contents of a
    479     * template that belongs to a state where it was a sub-template
    480     * with the SEC_ASN1_MAY_STREAM bit set and it's parent had the
    481     * optional bit set.  The information that the parent is optional
    482     * and that we should return the length of 0 when that length is
    483     * present since that means the optional field is no longer present.
    484     * So we add the disallowStreaming flag which is passed in when
    485     * writing the contents, but for all recursive calls to
    486     * sec_asn1e_contents_length, we pass PR_FALSE, because this
    487     * function correctly calculates the length for children templates
    488     * from that point on.  Confused yet?  At least you didn't have
    489     * to figure it out.  ;)  -javi
    490     */
    491    encode_kind = theTemplate->kind;
    492 
    493    universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
    494                    ? PR_TRUE
    495                    : PR_FALSE;
    496 
    497    isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
    498    encode_kind &= ~SEC_ASN1_EXPLICIT;
    499 
    500    optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
    501    encode_kind &= ~SEC_ASN1_OPTIONAL;
    502 
    503    PORT_Assert(!(isExplicit && universal)); /* bad templates */
    504 
    505    may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
    506    encode_kind &= ~SEC_ASN1_MAY_STREAM;
    507 
    508    /* Just clear this to get it out of the way; we do not need it here */
    509    encode_kind &= ~SEC_ASN1_DYNAMIC;
    510 
    511    if (encode_kind & SEC_ASN1_NO_STREAM) {
    512        disallowStreaming = PR_TRUE;
    513    }
    514    encode_kind &= ~SEC_ASN1_NO_STREAM;
    515 
    516    if (encode_kind & SEC_ASN1_CHOICE) {
    517        void *src2;
    518        int indx = sec_asn1e_which_choice(src, theTemplate);
    519        if (0 == indx) {
    520            /* XXX set an error? "choice not found" */
    521            /* state->top->status = encodeError; */
    522            return 0;
    523        }
    524 
    525        src2 = (void *)((char *)src - theTemplate->offset + theTemplate[indx].offset);
    526 
    527        return sec_asn1e_contents_length(&theTemplate[indx], src2,
    528                                         disallowStreaming, insideIndefinite,
    529                                         pHdrException);
    530    }
    531 
    532    if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || !universal) {
    533        /* XXX any bits we want to disallow (PORT_Assert against) here? */
    534        theTemplate = SEC_ASN1GetSubtemplate(theTemplate, src, PR_TRUE);
    535        if (encode_kind & SEC_ASN1_POINTER) {
    536            src = *(void **)src;
    537            if (src == NULL) {
    538                *pHdrException = optional ? hdr_optional : hdr_normal;
    539                return 0;
    540            }
    541        } else if (encode_kind & SEC_ASN1_INLINE) {
    542            /* check that there are no extraneous bits */
    543            if (optional) {
    544                if (PR_FALSE != SEC_ASN1IsTemplateSimple(theTemplate)) {
    545                    /* we now know that the target is a SECItem*, so we can check
    546                       if the source contains one */
    547                    SECItem *target = (SECItem *)src;
    548                    if (!target || !target->data || !target->len) {
    549                        /* no valid data to encode subtemplate */
    550                        *pHdrException = hdr_optional;
    551                        return 0;
    552                    }
    553                } else {
    554                    PORT_Assert(0); /* complex templates not handled as inline
    555                                       optional */
    556                }
    557            }
    558        }
    559 
    560        src = (char *)src + theTemplate->offset;
    561 
    562        /* recurse to find the length of the subtemplate */
    563        len = sec_asn1e_contents_length(theTemplate, src, disallowStreaming,
    564                                        insideIndefinite, pHdrException);
    565        if (len == 0 && optional) {
    566            *pHdrException = hdr_optional;
    567        } else if (isExplicit) {
    568            if (*pHdrException == hdr_any) {
    569                /* *we* do not want to add in a header,
    570                ** but our caller still does.
    571                */
    572                *pHdrException = hdr_normal;
    573            } else if (*pHdrException == hdr_normal) {
    574                /* if the inner content exists, our length is
    575                 * len(identifier) + len(length) + len(innercontent)
    576                 * XXX we currently assume len(identifier) == 1;
    577                 * to support a high-tag-number this would need to be smarter.
    578                 */
    579                len += 1 + SEC_ASN1LengthLength(len);
    580            }
    581        }
    582        return len;
    583    }
    584    underlying_kind = encode_kind;
    585 
    586    /* This is only used in decoding; it plays no part in encoding.  */
    587    if (underlying_kind & SEC_ASN1_SAVE) {
    588        /* check that there are no extraneous bits */
    589        PORT_Assert(underlying_kind == SEC_ASN1_SAVE);
    590        *pHdrException = hdr_decoder;
    591        return 0;
    592    }
    593 
    594 #define UNEXPECTED_FLAGS                                                          \
    595    (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_INLINE | SEC_ASN1_POINTER | \
    596     SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_SAVE | SEC_ASN1_SKIP)
    597 
    598    /* Having any of these bits is not expected here...  */
    599    PORT_Assert((underlying_kind & UNEXPECTED_FLAGS) == 0);
    600    underlying_kind &= ~UNEXPECTED_FLAGS;
    601 #undef UNEXPECTED_FLAGS
    602 
    603    if (underlying_kind & SEC_ASN1_CHOICE) {
    604        void *src2;
    605        int indx = sec_asn1e_which_choice(src, theTemplate);
    606        if (0 == indx) {
    607            /* XXX set an error? "choice not found" */
    608            /* state->top->status = encodeError; */
    609            return 0;
    610        }
    611 
    612        src2 = (void *)((char *)src - theTemplate->offset + theTemplate[indx].offset);
    613        len = sec_asn1e_contents_length(&theTemplate[indx], src2,
    614                                        disallowStreaming, insideIndefinite,
    615                                        pHdrException);
    616    } else {
    617        switch (underlying_kind) {
    618            case SEC_ASN1_SEQUENCE_OF:
    619            case SEC_ASN1_SET_OF: {
    620                const SEC_ASN1Template *tmpt;
    621                void *sub_src;
    622                unsigned long sub_len;
    623                void **group;
    624 
    625                len = 0;
    626 
    627                group = *(void ***)src;
    628                if (group == NULL)
    629                    break;
    630 
    631                tmpt = SEC_ASN1GetSubtemplate(theTemplate, src, PR_TRUE);
    632 
    633                for (; *group != NULL; group++) {
    634                    sub_src = (char *)(*group) + tmpt->offset;
    635                    sub_len = sec_asn1e_contents_length(tmpt, sub_src,
    636                                                        disallowStreaming,
    637                                                        insideIndefinite,
    638                                                        pHdrException);
    639                    len += sub_len;
    640                    /*
    641                     * XXX The 1 below is the presumed length of the identifier;
    642                     * to support a high-tag-number this would need to be smarter.
    643                     */
    644                    if (*pHdrException == hdr_normal)
    645                        len += 1 + SEC_ASN1LengthLength(sub_len);
    646                }
    647            } break;
    648 
    649            case SEC_ASN1_SEQUENCE:
    650            case SEC_ASN1_SET: {
    651                const SEC_ASN1Template *tmpt;
    652                void *sub_src;
    653                unsigned long sub_len;
    654 
    655                len = 0;
    656                for (tmpt = theTemplate + 1; tmpt->kind; tmpt++) {
    657                    sub_src = (char *)src + tmpt->offset;
    658                    sub_len = sec_asn1e_contents_length(tmpt, sub_src,
    659                                                        disallowStreaming,
    660                                                        insideIndefinite,
    661                                                        pHdrException);
    662                    len += sub_len;
    663                    /*
    664                     * XXX The 1 below is the presumed length of the identifier;
    665                     * to support a high-tag-number this would need to be smarter.
    666                     */
    667                    if (*pHdrException == hdr_normal)
    668                        len += 1 + SEC_ASN1LengthLength(sub_len);
    669                }
    670            } break;
    671 
    672            case SEC_ASN1_BIT_STRING:
    673                /* convert bit length to byte */
    674                len = (((SECItem *)src)->len + 7) >> 3;
    675                /* bit string contents involve an extra octet */
    676                if (len)
    677                    len++;
    678                break;
    679 
    680            case SEC_ASN1_INTEGER:
    681                /* ASN.1 INTEGERs are signed.
    682                 * If the source is an unsigned integer, the encoder will need
    683                 * to handle the conversion here.
    684                 */
    685                {
    686                    unsigned char *buf = ((SECItem *)src)->data;
    687                    SECItemType integerType = ((SECItem *)src)->type;
    688                    len = ((SECItem *)src)->len;
    689                    while (len > 0) {
    690                        if (*buf != 0) {
    691                            if (*buf & 0x80 && integerType == siUnsignedInteger) {
    692                                len++; /* leading zero needed to make number signed */
    693                            }
    694                            break; /* reached beginning of number */
    695                        }
    696                        if (len == 1) {
    697                            break; /* the number 0 */
    698                        }
    699                        if (buf[1] & 0x80) {
    700                            break; /* leading zero already present */
    701                        }
    702                        /* extraneous leading zero, keep going */
    703                        buf++;
    704                        len--;
    705                    }
    706                }
    707                break;
    708 
    709            default:
    710                len = ((SECItem *)src)->len;
    711                break;
    712        } /* end switch */
    713 
    714 #ifndef WHAT_PROBLEM_DOES_THIS_SOLVE
    715        /* if we're streaming, we may have a secitem w/len 0 as placeholder */
    716        if (!len && insideIndefinite && may_stream && !disallowStreaming) {
    717            len = 1;
    718        }
    719 #endif
    720    } /* end else */
    721 
    722    if (len == 0 && optional)
    723        *pHdrException = hdr_optional;
    724    else if (underlying_kind == SEC_ASN1_ANY)
    725        *pHdrException = hdr_any;
    726    else
    727        *pHdrException = hdr_normal;
    728 
    729    return len;
    730 }
    731 
    732 static void
    733 sec_asn1e_write_header(sec_asn1e_state *state)
    734 {
    735    unsigned long contents_length;
    736    unsigned char tag_number, tag_modifiers;
    737    sec_asn1e_hdr_encoding hdrException = hdr_normal;
    738    PRBool indefinite = PR_FALSE;
    739 
    740    PORT_Assert(state->place == beforeHeader);
    741 
    742    tag_number = state->tag_number;
    743    tag_modifiers = state->tag_modifiers;
    744 
    745    if (state->underlying_kind == SEC_ASN1_ANY) {
    746        state->place = duringContents;
    747        return;
    748    }
    749 
    750    if (state->underlying_kind & SEC_ASN1_CHOICE) {
    751        int indx = sec_asn1e_which_choice(state->src, state->theTemplate);
    752        if (0 == indx) {
    753            /* XXX set an error? "choice not found" */
    754            state->top->status = encodeError;
    755            return;
    756        }
    757        state->place = afterChoice;
    758        state = sec_asn1e_push_state(state->top, &state->theTemplate[indx],
    759                                     (char *)state->src - state->theTemplate->offset,
    760                                     PR_TRUE);
    761        if (state) {
    762            /*
    763             * Do the "before" field notification.
    764             */
    765            sec_asn1e_notify_before(state->top, state->src, state->depth);
    766            (void)sec_asn1e_init_state_based_on_template(state);
    767        }
    768        return;
    769    }
    770 
    771    /* The !isString test below is apparently intended to ensure that all
    772    ** constructed types receive indefinite length encoding.
    773    */
    774    indefinite = (PRBool)(state->top->streaming && state->may_stream &&
    775                          (state->top->from_buf || !state->is_string));
    776 
    777    /*
    778     * If we are doing a definite-length encoding, first we have to
    779     * walk the data structure to calculate the entire contents length.
    780     * If we are doing an indefinite-length encoding, we still need to
    781     * know if the contents is:
    782     *    optional and to be omitted, or
    783     *    an ANY (header is pre-encoded), or
    784     *    a SAVE or some other kind of template used only by the decoder.
    785     * So, we call this function either way.
    786     */
    787    contents_length = sec_asn1e_contents_length(state->theTemplate,
    788                                                state->src,
    789                                                state->disallowStreaming,
    790                                                indefinite,
    791                                                &hdrException);
    792    /*
    793     * We might be told explicitly not to put out a header.
    794     * But it can also be the case, via a pushed subtemplate, that
    795     * sec_asn1e_contents_length could not know that this field is
    796     * really optional.  So check for that explicitly, too.
    797     */
    798    if (hdrException != hdr_normal ||
    799        (contents_length == 0 && state->optional)) {
    800        state->place = afterContents;
    801        if (state->top->streaming &&
    802            state->may_stream &&
    803            state->top->from_buf) {
    804            /* we did not find an optional indefinite string, so we
    805             * don't encode it.  However, if TakeFromBuf is on, we stop
    806             * here anyway to give our caller a chance to intercept at the
    807             * same point where we would stop if the field were present.
    808             */
    809            state->top->status = needBytes;
    810        }
    811        return;
    812    }
    813 
    814    if (indefinite) {
    815        /*
    816         * We need to put out an indefinite-length encoding.
    817         * The only universal types that can be constructed are SETs,
    818         * SEQUENCEs, and strings; so check that it is one of those,
    819         * or that it is not universal (e.g. context-specific).
    820         */
    821        state->indefinite = PR_TRUE;
    822        PORT_Assert((tag_number == SEC_ASN1_SET) || (tag_number == SEC_ASN1_SEQUENCE) || ((tag_modifiers & SEC_ASN1_CLASS_MASK) != 0) || state->is_string);
    823        tag_modifiers |= SEC_ASN1_CONSTRUCTED;
    824        contents_length = 0;
    825    }
    826 
    827    sec_asn1e_write_identifier_bytes(state,
    828                                     (unsigned char)(tag_number | tag_modifiers));
    829    sec_asn1e_write_length_bytes(state, contents_length, state->indefinite);
    830 
    831    if (contents_length == 0 && !state->indefinite) {
    832        /*
    833         * If no real contents to encode, then we are done with this field.
    834         */
    835        state->place = afterContents;
    836        return;
    837    }
    838 
    839    /*
    840     * An EXPLICIT is nothing but an outer header, which we have already
    841     * written.  Now we need to do the inner header and contents.
    842     */
    843    if (state->isExplicit) {
    844        const SEC_ASN1Template *subt =
    845            SEC_ASN1GetSubtemplate(state->theTemplate, state->src, PR_TRUE);
    846        state->place = afterContents;
    847        state = sec_asn1e_push_state(state->top, subt, state->src, PR_TRUE);
    848        if (state != NULL) {
    849            (void)sec_asn1e_init_state_based_on_template(state);
    850        }
    851        return;
    852    }
    853 
    854    switch (state->underlying_kind) {
    855        case SEC_ASN1_SET_OF:
    856        case SEC_ASN1_SEQUENCE_OF:
    857            /*
    858             * We need to push a child to handle each member.
    859             */
    860            {
    861                void **group;
    862                const SEC_ASN1Template *subt;
    863 
    864                group = *(void ***)state->src;
    865                if (group == NULL || *group == NULL) {
    866                    /*
    867                     * Group is empty; we are done.
    868                     */
    869                    state->place = afterContents;
    870                    return;
    871                }
    872                state->place = duringGroup;
    873                subt = SEC_ASN1GetSubtemplate(state->theTemplate, state->src,
    874                                              PR_TRUE);
    875                state = sec_asn1e_push_state(state->top, subt, *group, PR_TRUE);
    876                if (state != NULL) {
    877                    (void)sec_asn1e_init_state_based_on_template(state);
    878                }
    879            }
    880            break;
    881 
    882        case SEC_ASN1_SEQUENCE:
    883        case SEC_ASN1_SET:
    884            /*
    885             * We need to push a child to handle the individual fields.
    886             */
    887            state->place = duringSequence;
    888            state = sec_asn1e_push_state(state->top, state->theTemplate + 1,
    889                                         state->src, PR_TRUE);
    890            if (state != NULL) {
    891                /*
    892                 * Do the "before" field notification.
    893                 */
    894                sec_asn1e_notify_before(state->top, state->src, state->depth);
    895                (void)sec_asn1e_init_state_based_on_template(state);
    896            }
    897            break;
    898 
    899        default:
    900            /*
    901             * I think we do not need to do anything else.
    902             * XXX Correct?
    903             */
    904            state->place = duringContents;
    905            break;
    906    }
    907 }
    908 
    909 static void
    910 sec_asn1e_write_contents_from_buf(sec_asn1e_state *state,
    911                                  const char *buf, unsigned long len)
    912 {
    913    PORT_Assert(state->place == duringContents);
    914    PORT_Assert(state->top->from_buf);
    915    PORT_Assert(state->may_stream && !state->disallowStreaming);
    916 
    917    /*
    918     * Probably they just turned on "take from buf", but have not
    919     * yet given us any bytes.  If there is nothing in the buffer
    920     * then we have nothing to do but return and wait.
    921     */
    922    if (buf == NULL || len == 0) {
    923        state->top->status = needBytes;
    924        return;
    925    }
    926    /*
    927     * We are streaming, reading from a passed-in buffer.
    928     * This means we are encoding a simple string or an ANY.
    929     * For the former, we need to put out a substring, with its
    930     * own identifier and length.  For an ANY, we just write it
    931     * out as is (our caller is required to ensure that it
    932     * is a properly encoded entity).
    933     */
    934    PORT_Assert(state->is_string); /* includes ANY */
    935    if (state->underlying_kind != SEC_ASN1_ANY) {
    936        unsigned char identifier;
    937 
    938        /*
    939         * Create the identifier based on underlying_kind.  We cannot
    940         * use tag_number and tag_modifiers because this can be an
    941         * implicitly encoded field.  In that case, the underlying
    942         * substrings *are* encoded with their real tag.
    943         */
    944        identifier = (unsigned char)(state->underlying_kind & SEC_ASN1_TAG_MASK);
    945        /*
    946         * The underlying kind should just be a simple string; there
    947         * should be no bits like CONTEXT_SPECIFIC or CONSTRUCTED set.
    948         */
    949        PORT_Assert((identifier & SEC_ASN1_TAGNUM_MASK) == identifier);
    950        /*
    951         * Write out the tag and length for the substring.
    952         */
    953        sec_asn1e_write_identifier_bytes(state, identifier);
    954        if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
    955            char byte;
    956            /*
    957             * Assume we have a length in bytes but we need to output
    958             * a proper bit string.  This interface only works for bit
    959             * strings that are full multiples of 8.  If support for
    960             * real, variable length bit strings is needed then the
    961             * caller will have to know to pass in a bit length instead
    962             * of a byte length and then this code will have to
    963             * perform the encoding necessary (length written is length
    964             * in bytes plus 1, and the first octet of string is the
    965             * number of bits remaining between the end of the bit
    966             * string and the next byte boundary).
    967             */
    968            sec_asn1e_write_length_bytes(state, len + 1, PR_FALSE);
    969            byte = 0;
    970            sec_asn1e_write_contents_bytes(state, &byte, 1);
    971        } else {
    972            sec_asn1e_write_length_bytes(state, len, PR_FALSE);
    973        }
    974    }
    975    sec_asn1e_write_contents_bytes(state, buf, len);
    976    state->top->status = needBytes;
    977 }
    978 
    979 static void
    980 sec_asn1e_write_contents(sec_asn1e_state *state)
    981 {
    982    unsigned long len = 0;
    983 
    984    PORT_Assert(state->place == duringContents);
    985 
    986    switch (state->underlying_kind) {
    987        case SEC_ASN1_SET:
    988        case SEC_ASN1_SEQUENCE:
    989            PORT_Assert(0);
    990            break;
    991 
    992        case SEC_ASN1_BIT_STRING: {
    993            SECItem *item;
    994            char rem;
    995 
    996            item = (SECItem *)state->src;
    997            len = (item->len + 7) >> 3;
    998            rem = (unsigned char)((len << 3) - item->len); /* remaining bits */
    999            sec_asn1e_write_contents_bytes(state, &rem, 1);
   1000            sec_asn1e_write_contents_bytes(state, (char *)item->data, len);
   1001        } break;
   1002 
   1003        case SEC_ASN1_BMP_STRING:
   1004            /* The number of bytes must be divisable by 2 */
   1005            if ((((SECItem *)state->src)->len) % 2) {
   1006                SEC_ASN1EncoderContext *cx;
   1007 
   1008                cx = state->top;
   1009                cx->status = encodeError;
   1010                break;
   1011            }
   1012            /* otherwise, fall through to write the content */
   1013            goto process_string;
   1014 
   1015        case SEC_ASN1_UNIVERSAL_STRING:
   1016            /* The number of bytes must be divisable by 4 */
   1017            if ((((SECItem *)state->src)->len) % 4) {
   1018                SEC_ASN1EncoderContext *cx;
   1019 
   1020                cx = state->top;
   1021                cx->status = encodeError;
   1022                break;
   1023            }
   1024            /* otherwise, fall through to write the content */
   1025            goto process_string;
   1026 
   1027        case SEC_ASN1_INTEGER:
   1028            /* ASN.1 INTEGERs are signed.  If the source is an unsigned
   1029             * integer, the encoder will need to handle the conversion here.
   1030             */
   1031            {
   1032                unsigned int blen;
   1033                unsigned char *buf;
   1034                SECItemType integerType;
   1035                blen = ((SECItem *)state->src)->len;
   1036                buf = ((SECItem *)state->src)->data;
   1037                integerType = ((SECItem *)state->src)->type;
   1038                while (blen > 0) {
   1039                    if (*buf & 0x80 && integerType == siUnsignedInteger) {
   1040                        char zero = 0; /* write a leading 0 */
   1041                        sec_asn1e_write_contents_bytes(state, &zero, 1);
   1042                        /* and then the remaining buffer */
   1043                        sec_asn1e_write_contents_bytes(state,
   1044                                                       (char *)buf, blen);
   1045                        break;
   1046                    }
   1047                    /* Check three possibilities:
   1048                     * 1.  No leading zeros, msb of MSB is not 1;
   1049                     * 2.  The number is zero itself;
   1050                     * 3.  Encoding a signed integer with a leading zero,
   1051                     *     keep the zero so that the number is positive.
   1052                     */
   1053                    if (*buf != 0 ||
   1054                        blen == 1 ||
   1055                        (buf[1] & 0x80 && integerType != siUnsignedInteger)) {
   1056                        sec_asn1e_write_contents_bytes(state,
   1057                                                       (char *)buf, blen);
   1058                        break;
   1059                    }
   1060                    /* byte is 0, continue */
   1061                    buf++;
   1062                    blen--;
   1063                }
   1064            }
   1065            /* done with this content */
   1066            break;
   1067 
   1068        process_string:
   1069        default: {
   1070            SECItem *item;
   1071 
   1072            item = (SECItem *)state->src;
   1073            sec_asn1e_write_contents_bytes(state, (char *)item->data,
   1074                                           item->len);
   1075        } break;
   1076    }
   1077    state->place = afterContents;
   1078 }
   1079 
   1080 /*
   1081 * We are doing a SET OF or SEQUENCE OF, and have just finished an item.
   1082 */
   1083 static void
   1084 sec_asn1e_next_in_group(sec_asn1e_state *state)
   1085 {
   1086    sec_asn1e_state *child;
   1087    void **group;
   1088    void *member;
   1089 
   1090    PORT_Assert(state->place == duringGroup);
   1091    PORT_Assert(state->child != NULL);
   1092 
   1093    child = state->child;
   1094 
   1095    group = *(void ***)state->src;
   1096 
   1097    /*
   1098     * Find placement of current item.
   1099     */
   1100    member = (char *)(state->child->src) - child->theTemplate->offset;
   1101    while (*group != member)
   1102        group++;
   1103 
   1104    /*
   1105     * Move forward to next item.
   1106     */
   1107    group++;
   1108    if (*group == NULL) {
   1109        /*
   1110         * That was our last one; we are done now.
   1111         */
   1112        child->place = notInUse;
   1113        state->place = afterContents;
   1114        return;
   1115    }
   1116    child->src = (char *)(*group) + child->theTemplate->offset;
   1117 
   1118    /*
   1119     * Re-"push" child.
   1120     */
   1121    sec_asn1e_scrub_state(child);
   1122    state->top->current = child;
   1123 }
   1124 
   1125 /*
   1126 * We are moving along through a sequence; move forward by one,
   1127 * (detecting end-of-sequence when it happens).
   1128 */
   1129 static void
   1130 sec_asn1e_next_in_sequence(sec_asn1e_state *state)
   1131 {
   1132    sec_asn1e_state *child;
   1133 
   1134    PORT_Assert(state->place == duringSequence);
   1135    PORT_Assert(state->child != NULL);
   1136 
   1137    child = state->child;
   1138 
   1139    /*
   1140     * Do the "after" field notification.
   1141     */
   1142    sec_asn1e_notify_after(state->top, child->src, child->depth);
   1143 
   1144    /*
   1145     * Move forward.
   1146     */
   1147    child->theTemplate++;
   1148    if (child->theTemplate->kind == 0) {
   1149        /*
   1150         * We are done with this sequence.
   1151         */
   1152        child->place = notInUse;
   1153        state->place = afterContents;
   1154        return;
   1155    }
   1156 
   1157    /*
   1158     * Reset state and push.
   1159     */
   1160 
   1161    child->src = (char *)state->src + child->theTemplate->offset;
   1162 
   1163    /*
   1164     * Do the "before" field notification.
   1165     */
   1166    sec_asn1e_notify_before(state->top, child->src, child->depth);
   1167 
   1168    state->top->current = child;
   1169    (void)sec_asn1e_init_state_based_on_template(child);
   1170 }
   1171 
   1172 static void
   1173 sec_asn1e_after_contents(sec_asn1e_state *state)
   1174 {
   1175    PORT_Assert(state->place == afterContents);
   1176 
   1177    if (state->indefinite)
   1178        sec_asn1e_write_end_of_contents_bytes(state);
   1179 
   1180    /*
   1181     * Just make my parent be the current state.  It will then clean
   1182     * up after me and free me (or reuse me).
   1183     */
   1184    state->top->current = state->parent;
   1185 }
   1186 
   1187 /*
   1188 * This function is called whether or not we are streaming; if we
   1189 * *are* streaming, our caller can also instruct us to take bytes
   1190 * from the passed-in buffer (at buf, for length len, which is likely
   1191 * bytes but could even mean bits if the current field is a bit string).
   1192 * If we have been so instructed, we will gobble up bytes from there
   1193 * (rather than from our src structure) and output them, and then
   1194 * we will just return, expecting to be called again -- either with
   1195 * more bytes or after our caller has instructed us that we are done
   1196 * (for now) with the buffer.
   1197 */
   1198 SECStatus
   1199 SEC_ASN1EncoderUpdate(SEC_ASN1EncoderContext *cx,
   1200                      const char *buf, unsigned long len)
   1201 {
   1202    sec_asn1e_state *state;
   1203 
   1204    if (cx->status == needBytes) {
   1205        cx->status = keepGoing;
   1206    }
   1207 
   1208    while (cx->status == keepGoing) {
   1209        state = cx->current;
   1210        switch (state->place) {
   1211            case beforeHeader:
   1212                sec_asn1e_write_header(state);
   1213                break;
   1214            case duringContents:
   1215                if (cx->from_buf)
   1216                    sec_asn1e_write_contents_from_buf(state, buf, len);
   1217                else
   1218                    sec_asn1e_write_contents(state);
   1219                break;
   1220            case duringGroup:
   1221                sec_asn1e_next_in_group(state);
   1222                break;
   1223            case duringSequence:
   1224                sec_asn1e_next_in_sequence(state);
   1225                break;
   1226            case afterContents:
   1227                sec_asn1e_after_contents(state);
   1228                break;
   1229            case afterImplicit:
   1230            case afterInline:
   1231            case afterPointer:
   1232            case afterChoice:
   1233                /*
   1234                 * These states are more documentation than anything.
   1235                 * They just need to force a pop.
   1236                 */
   1237                PORT_Assert(!state->indefinite);
   1238                state->place = afterContents;
   1239                break;
   1240            case notInUse:
   1241            default:
   1242                /* This is not an error, but rather a plain old BUG! */
   1243                PORT_Assert(0);
   1244                cx->status = encodeError;
   1245                break;
   1246        }
   1247 
   1248        if (cx->status == encodeError)
   1249            break;
   1250 
   1251        /* It might have changed, so we have to update our local copy.  */
   1252        state = cx->current;
   1253 
   1254        /* If it is NULL, we have popped all the way to the top.  */
   1255        if (state == NULL) {
   1256            cx->status = allDone;
   1257            break;
   1258        }
   1259    }
   1260 
   1261    if (cx->status == encodeError) {
   1262        return SECFailure;
   1263    }
   1264 
   1265    return SECSuccess;
   1266 }
   1267 
   1268 void
   1269 SEC_ASN1EncoderFinish(SEC_ASN1EncoderContext *cx)
   1270 {
   1271    /*
   1272     * XXX anything else that needs to be finished?
   1273     */
   1274 
   1275    PORT_FreeArena(cx->our_pool, PR_FALSE);
   1276 }
   1277 
   1278 SEC_ASN1EncoderContext *
   1279 SEC_ASN1EncoderStart(const void *src, const SEC_ASN1Template *theTemplate,
   1280                     SEC_ASN1WriteProc output_proc, void *output_arg)
   1281 {
   1282    PLArenaPool *our_pool;
   1283    SEC_ASN1EncoderContext *cx;
   1284 
   1285    our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
   1286    if (our_pool == NULL)
   1287        return NULL;
   1288 
   1289    cx = (SEC_ASN1EncoderContext *)PORT_ArenaZAlloc(our_pool, sizeof(*cx));
   1290    if (cx == NULL) {
   1291        PORT_FreeArena(our_pool, PR_FALSE);
   1292        return NULL;
   1293    }
   1294 
   1295    cx->our_pool = our_pool;
   1296    cx->output_proc = output_proc;
   1297    cx->output_arg = output_arg;
   1298 
   1299    cx->status = keepGoing;
   1300 
   1301    if (sec_asn1e_push_state(cx, theTemplate, src, PR_FALSE) == NULL ||
   1302        sec_asn1e_init_state_based_on_template(cx->current) == NULL) {
   1303        /*
   1304         * Trouble initializing (probably due to failed allocations)
   1305         * requires that we just give up.
   1306         */
   1307        PORT_FreeArena(our_pool, PR_FALSE);
   1308        return NULL;
   1309    }
   1310 
   1311    return cx;
   1312 }
   1313 
   1314 /*
   1315 * XXX Do we need a FilterProc, too?
   1316 */
   1317 
   1318 void
   1319 SEC_ASN1EncoderSetNotifyProc(SEC_ASN1EncoderContext *cx,
   1320                             SEC_ASN1NotifyProc fn, void *arg)
   1321 {
   1322    cx->notify_proc = fn;
   1323    cx->notify_arg = arg;
   1324 }
   1325 
   1326 void
   1327 SEC_ASN1EncoderClearNotifyProc(SEC_ASN1EncoderContext *cx)
   1328 {
   1329    cx->notify_proc = NULL;
   1330    cx->notify_arg = NULL; /* not necessary; just being clean */
   1331 }
   1332 
   1333 void
   1334 SEC_ASN1EncoderAbort(SEC_ASN1EncoderContext *cx, int error)
   1335 {
   1336    PORT_Assert(cx);
   1337    PORT_SetError(error);
   1338    cx->status = encodeError;
   1339 }
   1340 
   1341 void
   1342 SEC_ASN1EncoderSetStreaming(SEC_ASN1EncoderContext *cx)
   1343 {
   1344    /* XXX is there a way to check that we are "between" fields here? */
   1345 
   1346    cx->streaming = PR_TRUE;
   1347 }
   1348 
   1349 void
   1350 SEC_ASN1EncoderClearStreaming(SEC_ASN1EncoderContext *cx)
   1351 {
   1352    /* XXX is there a way to check that we are "between" fields here? */
   1353 
   1354    cx->streaming = PR_FALSE;
   1355 }
   1356 
   1357 void
   1358 SEC_ASN1EncoderSetTakeFromBuf(SEC_ASN1EncoderContext *cx)
   1359 {
   1360    /*
   1361     * XXX is there a way to check that we are "between" fields here?  this
   1362     * needs to include a check for being in between groups of items in
   1363     * a SET_OF or SEQUENCE_OF.
   1364     */
   1365    PORT_Assert(cx->streaming);
   1366 
   1367    cx->from_buf = PR_TRUE;
   1368 }
   1369 
   1370 void
   1371 SEC_ASN1EncoderClearTakeFromBuf(SEC_ASN1EncoderContext *cx)
   1372 {
   1373    /* we should actually be taking from buf *now* */
   1374    PORT_Assert(cx->from_buf);
   1375    if (!cx->from_buf) /* if not, just do nothing */
   1376        return;
   1377 
   1378    cx->from_buf = PR_FALSE;
   1379 
   1380    if (cx->status == needBytes) {
   1381        cx->status = keepGoing;
   1382        cx->current->place = afterContents;
   1383    }
   1384 }
   1385 
   1386 SECStatus
   1387 SEC_ASN1Encode(const void *src, const SEC_ASN1Template *theTemplate,
   1388               SEC_ASN1WriteProc output_proc, void *output_arg)
   1389 {
   1390    SEC_ASN1EncoderContext *ecx;
   1391    SECStatus rv;
   1392 
   1393    ecx = SEC_ASN1EncoderStart(src, theTemplate, output_proc, output_arg);
   1394    if (ecx == NULL)
   1395        return SECFailure;
   1396 
   1397    rv = SEC_ASN1EncoderUpdate(ecx, NULL, 0);
   1398 
   1399    SEC_ASN1EncoderFinish(ecx);
   1400    return rv;
   1401 }
   1402 
   1403 /*
   1404 * XXX depth and data_kind are unused; is there a PC way to silence warnings?
   1405 * (I mean "politically correct", not anything to do with intel/win platform)
   1406 */
   1407 static void
   1408 sec_asn1e_encode_item_count(void *arg, const char *buf, unsigned long len,
   1409                            int depth, SEC_ASN1EncodingPart data_kind)
   1410 {
   1411    unsigned long *count;
   1412 
   1413    count = (unsigned long *)arg;
   1414    PORT_Assert(count != NULL);
   1415 
   1416    *count += len;
   1417 }
   1418 
   1419 /* XXX depth and data_kind are unused; is there a PC way to silence warnings? */
   1420 static void
   1421 sec_asn1e_encode_item_store(void *arg, const char *buf, unsigned long len,
   1422                            int depth, SEC_ASN1EncodingPart data_kind)
   1423 {
   1424    SECItem *dest;
   1425 
   1426    dest = (SECItem *)arg;
   1427    PORT_Assert(dest != NULL);
   1428 
   1429    if (len > 0) {
   1430        PORT_Memcpy(dest->data + dest->len, buf, len);
   1431        dest->len += len;
   1432    }
   1433 }
   1434 
   1435 /*
   1436 * Allocate an entire SECItem, or just the data part of it, to hold
   1437 * "len" bytes of stuff.  Allocate from the given pool, if specified,
   1438 * otherwise just do a vanilla PORT_Alloc.
   1439 *
   1440 * XXX This seems like a reasonable general-purpose function (for SECITEM_)?
   1441 */
   1442 static SECItem *
   1443 sec_asn1e_allocate_item(PLArenaPool *poolp, SECItem *dest, unsigned long len)
   1444 {
   1445    if (poolp != NULL) {
   1446        void *release;
   1447 
   1448        release = PORT_ArenaMark(poolp);
   1449        if (dest == NULL)
   1450            dest = (SECItem *)PORT_ArenaAlloc(poolp, sizeof(SECItem));
   1451        if (dest != NULL) {
   1452            dest->data = (unsigned char *)PORT_ArenaAlloc(poolp, len);
   1453            if (dest->data == NULL) {
   1454                dest = NULL;
   1455            }
   1456        }
   1457        if (dest == NULL) {
   1458            /* one or both allocations failed; release everything */
   1459            PORT_ArenaRelease(poolp, release);
   1460        } else {
   1461            /* everything okay; unmark the arena */
   1462            PORT_ArenaUnmark(poolp, release);
   1463        }
   1464    } else {
   1465        SECItem *indest;
   1466 
   1467        indest = dest;
   1468        if (dest == NULL)
   1469            dest = (SECItem *)PORT_Alloc(sizeof(SECItem));
   1470        if (dest != NULL) {
   1471            dest->type = siBuffer;
   1472            dest->data = (unsigned char *)PORT_Alloc(len);
   1473            if (dest->data == NULL) {
   1474                if (indest == NULL)
   1475                    PORT_Free(dest);
   1476                dest = NULL;
   1477            }
   1478        }
   1479    }
   1480 
   1481    return dest;
   1482 }
   1483 
   1484 SECItem *
   1485 SEC_ASN1EncodeItem(PLArenaPool *poolp, SECItem *dest, const void *src,
   1486                   const SEC_ASN1Template *theTemplate)
   1487 {
   1488    unsigned long encoding_length;
   1489    SECStatus rv;
   1490 
   1491    PORT_Assert(dest == NULL || dest->data == NULL);
   1492 
   1493    encoding_length = 0;
   1494    rv = SEC_ASN1Encode(src, theTemplate,
   1495                        sec_asn1e_encode_item_count, &encoding_length);
   1496    if (rv != SECSuccess)
   1497        return NULL;
   1498 
   1499    dest = sec_asn1e_allocate_item(poolp, dest, encoding_length);
   1500    if (dest == NULL)
   1501        return NULL;
   1502 
   1503    /* XXX necessary?  This really just checks for a bug in the allocate fn */
   1504    PORT_Assert(dest->data != NULL);
   1505    if (dest->data == NULL)
   1506        return NULL;
   1507 
   1508    dest->len = 0;
   1509    (void)SEC_ASN1Encode(src, theTemplate, sec_asn1e_encode_item_store, dest);
   1510 
   1511    PORT_Assert(encoding_length == dest->len);
   1512    return dest;
   1513 }
   1514 
   1515 static SECItem *
   1516 sec_asn1e_integer(PLArenaPool *poolp, SECItem *dest, unsigned long value,
   1517                  PRBool is_unsigned)
   1518 {
   1519    unsigned long copy;
   1520    unsigned char sign;
   1521    int len = 0;
   1522 
   1523    /*
   1524     * Determine the length of the encoded value (minimum of 1).
   1525     */
   1526    copy = value;
   1527    do {
   1528        len++;
   1529        sign = (unsigned char)(copy & 0x80);
   1530        copy >>= 8;
   1531    } while (copy);
   1532 
   1533    /*
   1534     * If 'value' is non-negative, and the high bit of the last
   1535     * byte we counted was set, we need to add one to the length so
   1536     * we put a high-order zero byte in the encoding.
   1537     */
   1538    if (sign && (is_unsigned || (long)value >= 0))
   1539        len++;
   1540 
   1541    /*
   1542     * Allocate the item (if necessary) and the data pointer within.
   1543     */
   1544    dest = sec_asn1e_allocate_item(poolp, dest, len);
   1545    if (dest == NULL)
   1546        return NULL;
   1547 
   1548    /*
   1549     * Store the value, byte by byte, in the item.
   1550     */
   1551    dest->len = len;
   1552    while (len) {
   1553        dest->data[--len] = (unsigned char)value;
   1554        value >>= 8;
   1555    }
   1556    PORT_Assert(value == 0);
   1557 
   1558    return dest;
   1559 }
   1560 
   1561 SECItem *
   1562 SEC_ASN1EncodeInteger(PLArenaPool *poolp, SECItem *dest, long value)
   1563 {
   1564    return sec_asn1e_integer(poolp, dest, (unsigned long)value, PR_FALSE);
   1565 }
   1566 
   1567 SECItem *
   1568 SEC_ASN1EncodeUnsignedInteger(PLArenaPool *poolp,
   1569                              SECItem *dest, unsigned long value)
   1570 {
   1571    return sec_asn1e_integer(poolp, dest, value, PR_TRUE);
   1572 }