Back to index

lightning-sunbird  0.9+nobinonly
secasn1e.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * Support for ENcoding ASN.1 data based on BER/DER (Basic/Distinguished
00039  * Encoding Rules).
00040  *
00041  * $Id: secasn1e.c,v 1.20.2.1 2006/04/06 12:34:55 kaie%kuix.de Exp $
00042  */
00043 
00044 #include "secasn1.h"
00045 
00046 typedef enum {
00047     beforeHeader,
00048     duringContents,
00049     duringGroup,
00050     duringSequence,
00051     afterContents,
00052     afterImplicit,
00053     afterInline,
00054     afterPointer,
00055     afterChoice,
00056     notInUse
00057 } sec_asn1e_parse_place;
00058 
00059 typedef enum {
00060     allDone,
00061     encodeError,
00062     keepGoing,
00063     needBytes
00064 } sec_asn1e_parse_status;
00065 
00066 typedef enum {
00067     hdr_normal      = 0,  /* encode header normally */
00068     hdr_any         = 1,  /* header already encoded in content */
00069     hdr_decoder     = 2,  /* template only used by decoder. skip it. */
00070     hdr_optional    = 3,  /* optional component, to be omitted */
00071     hdr_placeholder = 4   /* place holder for from_buf content */
00072 } sec_asn1e_hdr_encoding;
00073 
00074 typedef struct sec_asn1e_state_struct {
00075     SEC_ASN1EncoderContext *top;
00076     const SEC_ASN1Template *theTemplate;
00077     void *src;
00078 
00079     struct sec_asn1e_state_struct *parent;       /* aka prev */
00080     struct sec_asn1e_state_struct *child; /* aka next */
00081 
00082     sec_asn1e_parse_place place;   /* where we are in encoding process */
00083 
00084     /*
00085      * XXX explain the next fields as clearly as possible...
00086      */
00087     unsigned char tag_modifiers;
00088     unsigned char tag_number;
00089     unsigned long underlying_kind;
00090 
00091     int depth;
00092 
00093     PRBool isExplicit,             /* we are handling an isExplicit header */
00094           indefinite,              /* need end-of-contents */
00095           is_string,        /* encoding a simple string or an ANY */
00096           may_stream,              /* when streaming, do indefinite encoding */
00097           optional,         /* omit field if it has no contents */
00098           disallowStreaming;       /* disallow streaming in all sub-templates */    
00099 } sec_asn1e_state;
00100 
00101 /*
00102  * An "outsider" will have an opaque pointer to this, created by calling
00103  * SEC_ASN1EncoderStart().  It will be passed back in to all subsequent
00104  * calls to SEC_ASN1EncoderUpdate() and related routines, and when done
00105  * it is passed to SEC_ASN1EncoderFinish().
00106  */
00107 struct sec_EncoderContext_struct {
00108     PRArenaPool *our_pool;         /* for our internal allocs */
00109 
00110     sec_asn1e_state *current;
00111     sec_asn1e_parse_status status;
00112 
00113     PRBool streaming;
00114     PRBool from_buf;
00115 
00116     SEC_ASN1NotifyProc notify_proc;       /* call before/after handling field */
00117     void *notify_arg;                     /* argument to notify_proc */
00118     PRBool during_notify;          /* true during call to notify_proc */
00119 
00120     SEC_ASN1WriteProc output_proc; /* pass encoded bytes to this  */
00121     void *output_arg;                     /* argument to that function */
00122 };
00123 
00124 
00125 static sec_asn1e_state *
00126 sec_asn1e_push_state (SEC_ASN1EncoderContext *cx,
00127                     const SEC_ASN1Template *theTemplate,
00128                     const void *src, PRBool new_depth)
00129 {
00130     sec_asn1e_state *state, *new_state;
00131 
00132     state = cx->current;
00133 
00134     new_state = (sec_asn1e_state*)PORT_ArenaZAlloc (cx->our_pool, 
00135                                               sizeof(*new_state));
00136     if (new_state == NULL) {
00137        cx->status = encodeError;
00138        return NULL;
00139     }
00140 
00141     new_state->top = cx;
00142     new_state->parent = state;
00143     new_state->theTemplate = theTemplate;
00144     new_state->place = notInUse;
00145     if (src != NULL)
00146        new_state->src = (char *)src + theTemplate->offset;
00147 
00148     if (state != NULL) {
00149        new_state->depth = state->depth;
00150        if (new_depth)
00151            new_state->depth++;
00152        state->child = new_state;
00153     }
00154 
00155     cx->current = new_state;
00156     return new_state;
00157 }
00158 
00159 
00160 static void
00161 sec_asn1e_scrub_state (sec_asn1e_state *state)
00162 {
00163     /*
00164      * Some default "scrubbing".
00165      * XXX right set of initializations?
00166      */
00167     state->place = beforeHeader;
00168     state->indefinite = PR_FALSE;
00169 }
00170 
00171 
00172 static void
00173 sec_asn1e_notify_before (SEC_ASN1EncoderContext *cx, void *src, int depth)
00174 {
00175     if (cx->notify_proc == NULL)
00176        return;
00177 
00178     cx->during_notify = PR_TRUE;
00179     (* cx->notify_proc) (cx->notify_arg, PR_TRUE, src, depth);
00180     cx->during_notify = PR_FALSE;
00181 }
00182 
00183 
00184 static void
00185 sec_asn1e_notify_after (SEC_ASN1EncoderContext *cx, void *src, int depth)
00186 {
00187     if (cx->notify_proc == NULL)
00188        return;
00189 
00190     cx->during_notify = PR_TRUE;
00191     (* cx->notify_proc) (cx->notify_arg, PR_FALSE, src, depth);
00192     cx->during_notify = PR_FALSE;
00193 }
00194 
00195 
00196 static sec_asn1e_state *
00197 sec_asn1e_init_state_based_on_template (sec_asn1e_state *state)
00198 {
00199     PRBool isExplicit, is_string, may_stream, optional, universal; 
00200     PRBool disallowStreaming;
00201     unsigned char tag_modifiers;
00202     unsigned long encode_kind, under_kind;
00203     unsigned long tag_number;
00204     PRBool isInline = PR_FALSE;
00205 
00206 
00207     encode_kind = state->theTemplate->kind;
00208 
00209     universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
00210               ? PR_TRUE : PR_FALSE;
00211 
00212     isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
00213     encode_kind &= ~SEC_ASN1_EXPLICIT;
00214 
00215     optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
00216     encode_kind &= ~SEC_ASN1_OPTIONAL;
00217 
00218     PORT_Assert (!(isExplicit && universal));    /* bad templates */
00219 
00220     may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
00221     encode_kind &= ~SEC_ASN1_MAY_STREAM;
00222 
00223     disallowStreaming = (encode_kind & SEC_ASN1_NO_STREAM) ? PR_TRUE : PR_FALSE;
00224     encode_kind &= ~SEC_ASN1_NO_STREAM;
00225 
00226     /* Just clear this to get it out of the way; we do not need it here */
00227     encode_kind &= ~SEC_ASN1_DYNAMIC;
00228 
00229     if( encode_kind & SEC_ASN1_CHOICE ) {
00230       under_kind = SEC_ASN1_CHOICE;
00231     } else if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || 
00232         (!universal && !isExplicit)) {
00233        const SEC_ASN1Template *subt;
00234        void *src = NULL;
00235 
00236        PORT_Assert ((encode_kind & (SEC_ASN1_ANY | SEC_ASN1_SKIP)) == 0);
00237 
00238        sec_asn1e_scrub_state (state);
00239 
00240        if (encode_kind & SEC_ASN1_POINTER) {
00241            src = *(void **)state->src;
00242            state->place = afterPointer;
00243 
00244            if (src == NULL) {
00245               /*
00246                * If this is optional, but NULL, then the field does
00247                * not need to be encoded.  In this case we are done;
00248                * we do not want to push a subtemplate.
00249                */
00250               if (optional)
00251                   return state;
00252 
00253               /*
00254                * XXX this is an error; need to figure out
00255                * how to handle this
00256                */
00257            }
00258        } else {
00259            src = state->src;
00260            if (encode_kind & SEC_ASN1_INLINE) {
00261               /* check that there are no extraneous bits */
00262               /* PORT_Assert (encode_kind == SEC_ASN1_INLINE && !optional); */
00263               state->place = afterInline;
00264               isInline = PR_TRUE;
00265            } else {
00266               /*
00267                * Save the tag modifiers and tag number here before moving
00268                * on to the next state in case this is a member of a
00269                * SEQUENCE OF
00270                */
00271               state->tag_modifiers = (unsigned char)
00272                   (encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK));
00273               state->tag_number = (unsigned char)
00274                   (encode_kind & SEC_ASN1_TAGNUM_MASK);
00275               
00276               state->place = afterImplicit;
00277               state->optional = optional;
00278            }
00279        }
00280 
00281        subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src, PR_TRUE);
00282        if (isInline && optional) {
00283            /* we only handle a very limited set of optional inline cases at
00284               this time */
00285            if (PR_FALSE != SEC_ASN1IsTemplateSimple(subt)) {
00286               /* we now know that the target is a SECItem*, so we can check
00287                  if the source contains one */
00288               SECItem* target = (SECItem*)state->src;
00289               if (!target || !target->data || !target->len) {
00290                   /* no valid data to encode subtemplate */
00291                   return state;
00292               }
00293            } else {
00294               PORT_Assert(0); /* complex templates are not handled as
00295                                inline optional */
00296            }
00297        }
00298        state = sec_asn1e_push_state (state->top, subt, src, PR_FALSE);
00299        if (state == NULL)
00300            return state;
00301 
00302        if (universal) {
00303            /*
00304             * This is a POINTER or INLINE; just init based on that
00305             * and we are done.
00306             */
00307            return sec_asn1e_init_state_based_on_template (state);
00308        }
00309 
00310        /*
00311         * This is an implicit, non-universal (meaning, application-private
00312         * or context-specific) field.  This results in a "magic" tag but
00313         * encoding based on the underlying type.  We pushed a new state
00314         * that is based on the subtemplate (the underlying type), but
00315         * now we will sort of alias it to give it some of our properties
00316         * (tag, optional status, etc.).
00317         *
00318         * NB: ALL the following flags in the subtemplate are disallowed
00319         *     and/or ignored: EXPLICIT, OPTIONAL, INNER, INLINE, POINTER.
00320         */
00321 
00322        under_kind = state->theTemplate->kind;
00323        if ((under_kind & SEC_ASN1_MAY_STREAM) && !disallowStreaming) {
00324            may_stream = PR_TRUE;
00325        }
00326        under_kind &= ~(SEC_ASN1_MAY_STREAM | SEC_ASN1_DYNAMIC);
00327     } else {
00328        under_kind = encode_kind;
00329     }
00330 
00331     /*
00332      * Sanity check that there are no unwanted bits marked in under_kind.
00333      * These bits were either removed above (after we recorded them) or
00334      * they simply should not be found (signalling a bad/broken template).
00335      * XXX is this the right set of bits to test here? (i.e. need to add
00336      * or remove any?)
00337      */
00338 #define UNEXPECTED_FLAGS \
00339  (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_SKIP | SEC_ASN1_INNER | \
00340   SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_INLINE | SEC_ASN1_POINTER)
00341 
00342     PORT_Assert ((under_kind & UNEXPECTED_FLAGS) == 0);
00343     under_kind &= ~UNEXPECTED_FLAGS;
00344 #undef UNEXPECTED_FLAGS
00345 
00346     if (encode_kind & SEC_ASN1_ANY) {
00347        PORT_Assert (encode_kind == under_kind);
00348        tag_modifiers = 0;
00349        tag_number = 0;
00350        is_string = PR_TRUE;
00351     } else {
00352        tag_modifiers = (unsigned char)
00353               (encode_kind & (SEC_ASN1_TAG_MASK & ~SEC_ASN1_TAGNUM_MASK));
00354        /*
00355         * XXX This assumes only single-octet identifiers.  To handle
00356         * the HIGH TAG form we would need to do some more work, especially
00357         * in how to specify them in the template, because right now we
00358         * do not provide a way to specify more *tag* bits in encode_kind.
00359         */
00360        tag_number = encode_kind & SEC_ASN1_TAGNUM_MASK;
00361 
00362        is_string = PR_FALSE;
00363        switch (under_kind & SEC_ASN1_TAGNUM_MASK) {
00364          case SEC_ASN1_SET:
00365            /*
00366             * XXX A plain old SET (as opposed to a SET OF) is not implemented.
00367             * If it ever is, remove this assert...
00368             */
00369            PORT_Assert ((under_kind & SEC_ASN1_GROUP) != 0);
00370            /* fallthru */
00371          case SEC_ASN1_SEQUENCE:
00372            tag_modifiers |= SEC_ASN1_CONSTRUCTED;
00373            break;
00374          case SEC_ASN1_BIT_STRING:
00375          case SEC_ASN1_BMP_STRING: 
00376          case SEC_ASN1_GENERALIZED_TIME:
00377          case SEC_ASN1_IA5_STRING:
00378          case SEC_ASN1_OCTET_STRING:
00379          case SEC_ASN1_PRINTABLE_STRING:
00380          case SEC_ASN1_T61_STRING:
00381          case SEC_ASN1_UNIVERSAL_STRING: 
00382          case SEC_ASN1_UTC_TIME:
00383          case SEC_ASN1_UTF8_STRING:
00384          case SEC_ASN1_VISIBLE_STRING: 
00385            /*
00386             * We do not yet know if we will be constructing the string,
00387             * so we have to wait to do this final tag modification.
00388             */
00389            is_string = PR_TRUE;
00390            break;
00391        }
00392     }
00393 
00394     state->tag_modifiers = tag_modifiers;
00395     state->tag_number = (unsigned char)tag_number;
00396     state->underlying_kind = under_kind;
00397     state->isExplicit = isExplicit;
00398     state->may_stream = may_stream;
00399     state->is_string = is_string;
00400     state->optional = optional;
00401     state->disallowStreaming = disallowStreaming;
00402 
00403     sec_asn1e_scrub_state (state);
00404 
00405     return state;
00406 }
00407 
00408 
00409 static void
00410 sec_asn1e_write_part (sec_asn1e_state *state,
00411                     const char *buf, unsigned long len,
00412                     SEC_ASN1EncodingPart part)
00413 {
00414     SEC_ASN1EncoderContext *cx;
00415 
00416     cx = state->top;
00417     (* cx->output_proc) (cx->output_arg, buf, len, state->depth, part);
00418 }
00419 
00420 
00421 /*
00422  * XXX This assumes only single-octet identifiers.  To handle
00423  * the HIGH TAG form we would need to modify this interface and
00424  * teach it to properly encode the special form.
00425  */
00426 static void
00427 sec_asn1e_write_identifier_bytes (sec_asn1e_state *state, unsigned char value)
00428 {
00429     char byte;
00430 
00431     byte = (char) value;
00432     sec_asn1e_write_part (state, &byte, 1, SEC_ASN1_Identifier);
00433 }
00434 
00435 int
00436 SEC_ASN1EncodeLength(unsigned char *buf,int value) {
00437     int lenlen;
00438 
00439     lenlen = SEC_ASN1LengthLength (value);
00440     if (lenlen == 1) {
00441        buf[0] = value;
00442     } else {
00443        int i;
00444 
00445        i = lenlen - 1;
00446        buf[0] = 0x80 | i;
00447        while (i) {
00448            buf[i--] = value;
00449            value >>= 8;
00450        }
00451         PORT_Assert (value == 0);
00452     }
00453     return lenlen;
00454 }
00455 
00456 static void
00457 sec_asn1e_write_length_bytes (sec_asn1e_state *state, unsigned long value,
00458                            PRBool indefinite)
00459 {
00460     int lenlen;
00461     unsigned char buf[sizeof(unsigned long) + 1];
00462 
00463     if (indefinite) {
00464        PORT_Assert (value == 0);
00465        buf[0] = 0x80;
00466        lenlen = 1;
00467     } else {
00468        lenlen = SEC_ASN1EncodeLength(buf,value);
00469     }
00470 
00471     sec_asn1e_write_part (state, (char *) buf, lenlen, SEC_ASN1_Length);
00472 }
00473 
00474 
00475 static void
00476 sec_asn1e_write_contents_bytes (sec_asn1e_state *state,
00477                             const char *buf, unsigned long len)
00478 {
00479     sec_asn1e_write_part (state, buf, len, SEC_ASN1_Contents);
00480 }
00481 
00482 
00483 static void
00484 sec_asn1e_write_end_of_contents_bytes (sec_asn1e_state *state)
00485 {
00486     const char eoc[2] = {0, 0};
00487 
00488     sec_asn1e_write_part (state, eoc, 2, SEC_ASN1_EndOfContents);
00489 }
00490 
00491 static int
00492 sec_asn1e_which_choice
00493 (
00494   void *src,
00495   const SEC_ASN1Template *theTemplate
00496 )
00497 {
00498   int rv;
00499   unsigned int which = *(unsigned int *)src;
00500 
00501   for( rv = 1, theTemplate++; theTemplate->kind != 0; rv++, theTemplate++ ) {
00502     if( which == theTemplate->size ) {
00503       return rv;
00504     }
00505   }
00506 
00507   return 0;
00508 }
00509 
00510 static unsigned long
00511 sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,
00512                         PRBool disallowStreaming, PRBool insideIndefinite,
00513                         sec_asn1e_hdr_encoding *pHdrException)
00514 {
00515     unsigned long encode_kind, underlying_kind;
00516     PRBool isExplicit, optional, universal, may_stream;
00517     unsigned long len;
00518 
00519     /*
00520      * This function currently calculates the length in all cases
00521      * except the following: when writing out the contents of a 
00522      * template that belongs to a state where it was a sub-template
00523      * with the SEC_ASN1_MAY_STREAM bit set and it's parent had the
00524      * optional bit set.  The information that the parent is optional
00525      * and that we should return the length of 0 when that length is 
00526      * present since that means the optional field is no longer present.
00527      * So we add the disallowStreaming flag which is passed in when
00528      * writing the contents, but for all recursive calls to 
00529      * sec_asn1e_contents_length, we pass PR_FALSE, because this
00530      * function correctly calculates the length for children templates
00531      * from that point on.  Confused yet?  At least you didn't have
00532      * to figure it out.  ;)  -javi
00533      */
00534     encode_kind = theTemplate->kind;
00535 
00536     universal = ((encode_kind & SEC_ASN1_CLASS_MASK) == SEC_ASN1_UNIVERSAL)
00537               ? PR_TRUE : PR_FALSE;
00538 
00539     isExplicit = (encode_kind & SEC_ASN1_EXPLICIT) ? PR_TRUE : PR_FALSE;
00540     encode_kind &= ~SEC_ASN1_EXPLICIT;
00541 
00542     optional = (encode_kind & SEC_ASN1_OPTIONAL) ? PR_TRUE : PR_FALSE;
00543     encode_kind &= ~SEC_ASN1_OPTIONAL;
00544 
00545     PORT_Assert (!(isExplicit && universal));    /* bad templates */
00546 
00547     may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
00548     encode_kind &= ~SEC_ASN1_MAY_STREAM;
00549 
00550     /* Just clear this to get it out of the way; we do not need it here */
00551     encode_kind &= ~SEC_ASN1_DYNAMIC;
00552 
00553     if (encode_kind & SEC_ASN1_NO_STREAM) {
00554        disallowStreaming = PR_TRUE;
00555     }
00556     encode_kind &= ~SEC_ASN1_NO_STREAM;
00557 
00558     if (encode_kind & SEC_ASN1_CHOICE) {
00559        void *src2;
00560        int indx = sec_asn1e_which_choice(src, theTemplate);
00561        if (0 == indx) {
00562            /* XXX set an error? "choice not found" */
00563            /* state->top->status = encodeError; */
00564            return 0;
00565        }
00566 
00567         src2 = (void *)
00568                ((char *)src - theTemplate->offset + theTemplate[indx].offset);
00569 
00570         return sec_asn1e_contents_length(&theTemplate[indx], src2, 
00571                                     disallowStreaming, insideIndefinite,
00572                                     pHdrException);
00573     }
00574 
00575     if ((encode_kind & (SEC_ASN1_POINTER | SEC_ASN1_INLINE)) || !universal) {
00576        /* XXX any bits we want to disallow (PORT_Assert against) here? */
00577        theTemplate = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE);
00578        if (encode_kind & SEC_ASN1_POINTER) {
00579            src = *(void **)src;
00580            if (src == NULL) {
00581               *pHdrException = optional ? hdr_optional : hdr_normal;
00582               return 0;
00583            }
00584        } else if (encode_kind & SEC_ASN1_INLINE) {
00585            /* check that there are no extraneous bits */
00586            if (optional) {
00587               if (PR_FALSE != SEC_ASN1IsTemplateSimple(theTemplate)) {
00588                   /* we now know that the target is a SECItem*, so we can check
00589                      if the source contains one */
00590                   SECItem* target = (SECItem*)src;
00591                   if (!target || !target->data || !target->len) {
00592                      /* no valid data to encode subtemplate */
00593                      *pHdrException = hdr_optional;
00594                      return 0;
00595                   }
00596               } else {
00597                   PORT_Assert(0); /* complex templates not handled as inline
00598                                        optional */
00599               }
00600            }
00601        }
00602 
00603        src = (char *)src + theTemplate->offset;
00604 
00605        /* recurse to find the length of the subtemplate */
00606        len = sec_asn1e_contents_length (theTemplate, src, disallowStreaming, 
00607                                         insideIndefinite, pHdrException);
00608        if (len == 0 && optional) {
00609            *pHdrException = hdr_optional;
00610        } else if (isExplicit) {
00611            if (*pHdrException == hdr_any) {
00612               /* *we* do not want to add in a header, 
00613               ** but our caller still does. 
00614               */
00615               *pHdrException = hdr_normal;
00616            } else if (*pHdrException == hdr_normal) {
00617               /* if the inner content exists, our length is
00618                * len(identifier) + len(length) + len(innercontent)
00619                * XXX we currently assume len(identifier) == 1;
00620                * to support a high-tag-number this would need to be smarter.
00621                */
00622               len += 1 + SEC_ASN1LengthLength (len);
00623            }
00624        }
00625        return len;
00626     }
00627     underlying_kind = encode_kind;
00628 
00629     /* This is only used in decoding; it plays no part in encoding.  */
00630     if (underlying_kind & SEC_ASN1_SAVE) {
00631        /* check that there are no extraneous bits */
00632        PORT_Assert (underlying_kind == SEC_ASN1_SAVE);
00633        *pHdrException = hdr_decoder;
00634        return 0;
00635     }
00636 
00637 #define UNEXPECTED_FLAGS \
00638  (SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_INLINE | SEC_ASN1_POINTER |\
00639   SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM | SEC_ASN1_SAVE | SEC_ASN1_SKIP)
00640 
00641     /* Having any of these bits is not expected here...  */
00642     PORT_Assert ((underlying_kind & UNEXPECTED_FLAGS) == 0);
00643     underlying_kind &= ~UNEXPECTED_FLAGS;
00644 #undef UNEXPECTED_FLAGS
00645 
00646     if (underlying_kind & SEC_ASN1_CHOICE) {
00647        void *src2;
00648        int indx = sec_asn1e_which_choice(src, theTemplate);
00649        if (0 == indx) {
00650            /* XXX set an error? "choice not found" */
00651            /* state->top->status = encodeError; */
00652            return 0;
00653        }
00654 
00655         src2 = (void *)
00656               ((char *)src - theTemplate->offset + theTemplate[indx].offset);
00657         len = sec_asn1e_contents_length(&theTemplate[indx], src2, 
00658                                        disallowStreaming, insideIndefinite, 
00659                                    pHdrException);
00660     } else {
00661       switch (underlying_kind) {
00662       case SEC_ASN1_SEQUENCE_OF:
00663       case SEC_ASN1_SET_OF:
00664        {
00665            const SEC_ASN1Template *tmpt;
00666            void *sub_src;
00667            unsigned long sub_len;
00668            void **group;
00669 
00670            len = 0;
00671 
00672            group = *(void ***)src;
00673            if (group == NULL)
00674               break;
00675 
00676            tmpt = SEC_ASN1GetSubtemplate (theTemplate, src, PR_TRUE);
00677 
00678            for (; *group != NULL; group++) {
00679               sub_src = (char *)(*group) + tmpt->offset;
00680               sub_len = sec_asn1e_contents_length (tmpt, sub_src, 
00681                                                    disallowStreaming,
00682                                                insideIndefinite,
00683                                                      pHdrException);
00684               len += sub_len;
00685               /*
00686                * XXX The 1 below is the presumed length of the identifier;
00687                * to support a high-tag-number this would need to be smarter.
00688                */
00689               if (*pHdrException == hdr_normal)
00690                   len += 1 + SEC_ASN1LengthLength (sub_len);
00691            }
00692        }
00693        break;
00694 
00695       case SEC_ASN1_SEQUENCE:
00696       case SEC_ASN1_SET:
00697        {
00698            const SEC_ASN1Template *tmpt;
00699            void *sub_src;
00700            unsigned long sub_len;
00701 
00702            len = 0;
00703            for (tmpt = theTemplate + 1; tmpt->kind; tmpt++) {
00704               sub_src = (char *)src + tmpt->offset;
00705               sub_len = sec_asn1e_contents_length (tmpt, sub_src, 
00706                                                    disallowStreaming,
00707                                                insideIndefinite,
00708                                                      pHdrException);
00709               len += sub_len;
00710               /*
00711                * XXX The 1 below is the presumed length of the identifier;
00712                * to support a high-tag-number this would need to be smarter.
00713                */
00714               if (*pHdrException == hdr_normal)
00715                   len += 1 + SEC_ASN1LengthLength (sub_len);
00716            }
00717        }
00718        break;
00719 
00720       case SEC_ASN1_BIT_STRING:
00721        /* convert bit length to byte */
00722        len = (((SECItem *)src)->len + 7) >> 3;
00723        /* bit string contents involve an extra octet */
00724        if (len)
00725            len++;
00726        break;
00727 
00728       case SEC_ASN1_INTEGER:
00729        /* ASN.1 INTEGERs are signed.
00730         * If the source is an unsigned integer, the encoder will need 
00731         * to handle the conversion here.
00732         */
00733        {
00734            unsigned char *buf = ((SECItem *)src)->data;
00735            SECItemType integerType = ((SECItem *)src)->type;
00736            len = ((SECItem *)src)->len;
00737            while (len > 0) {
00738               if (*buf != 0) {
00739                   if (*buf & 0x80 && integerType == siUnsignedInteger) {
00740                      len++; /* leading zero needed to make number signed */
00741                   }
00742                   break; /* reached beginning of number */
00743               }
00744               if (len == 1) {
00745                   break; /* the number 0 */
00746               }
00747               if (buf[1] & 0x80) {
00748                   break; /* leading zero already present */
00749               } 
00750               /* extraneous leading zero, keep going */
00751               buf++;
00752               len--;
00753            }
00754        }
00755        break;
00756 
00757       default:
00758        len = ((SECItem *)src)->len;
00759        break;
00760       }  /* end switch */
00761 
00762 #ifndef WHAT_PROBLEM_DOES_THIS_SOLVE
00763       /* if we're streaming, we may have a secitem w/len 0 as placeholder */
00764       if (!len && insideIndefinite && may_stream && !disallowStreaming) {
00765          len = 1;
00766       }
00767 #endif
00768     }    /* end else */
00769 
00770     if (len == 0 && optional)
00771        *pHdrException = hdr_optional;
00772     else if (underlying_kind == SEC_ASN1_ANY)
00773        *pHdrException = hdr_any;
00774     else 
00775        *pHdrException = hdr_normal;
00776 
00777     return len;
00778 }
00779 
00780 
00781 static void
00782 sec_asn1e_write_header (sec_asn1e_state *state)
00783 {
00784     unsigned long contents_length;
00785     unsigned char tag_number, tag_modifiers;
00786     sec_asn1e_hdr_encoding hdrException = hdr_normal;
00787     PRBool indefinite = PR_FALSE;
00788 
00789     PORT_Assert (state->place == beforeHeader);
00790 
00791     tag_number = state->tag_number;
00792     tag_modifiers = state->tag_modifiers;
00793 
00794     if (state->underlying_kind == SEC_ASN1_ANY) {
00795        state->place = duringContents;
00796        return;
00797     }
00798 
00799     if (state->underlying_kind & SEC_ASN1_CHOICE) {
00800        int indx = sec_asn1e_which_choice(state->src, state->theTemplate);
00801        if( 0 == indx ) {
00802            /* XXX set an error? "choice not found" */
00803            state->top->status = encodeError;
00804            return;
00805        }
00806        state->place = afterChoice;
00807        state = sec_asn1e_push_state(state->top, &state->theTemplate[indx],
00808                             (char *)state->src - state->theTemplate->offset, 
00809                             PR_TRUE);
00810        if (state) {
00811            /*
00812             * Do the "before" field notification.
00813             */
00814            sec_asn1e_notify_before (state->top, state->src, state->depth);
00815            state = sec_asn1e_init_state_based_on_template (state);
00816        }
00817        return;
00818     }
00819 
00820     /* The !isString test below is apparently intended to ensure that all 
00821     ** constructed types receive indefinite length encoding.
00822     */
00823    indefinite = (PRBool) 
00824        (state->top->streaming && state->may_stream && 
00825         (state->top->from_buf || !state->is_string));
00826 
00827     /*
00828      * If we are doing a definite-length encoding, first we have to
00829      * walk the data structure to calculate the entire contents length.
00830      * If we are doing an indefinite-length encoding, we still need to 
00831      * know if the contents is:
00832      *    optional and to be omitted, or 
00833      *    an ANY (header is pre-encoded), or 
00834      *    a SAVE or some other kind of template used only by the decoder.
00835      * So, we call this function either way.
00836      */
00837     contents_length = sec_asn1e_contents_length (state->theTemplate,
00838                                            state->src, 
00839                                                  state->disallowStreaming,
00840                                            indefinite,
00841                                                  &hdrException);
00842     /*
00843      * We might be told explicitly not to put out a header.
00844      * But it can also be the case, via a pushed subtemplate, that
00845      * sec_asn1e_contents_length could not know that this field is
00846      * really optional.  So check for that explicitly, too.
00847      */
00848     if (hdrException != hdr_normal || 
00849        (contents_length == 0 && state->optional)) {
00850        state->place = afterContents;
00851        if (state->top->streaming && 
00852            state->may_stream && 
00853            state->top->from_buf) {
00854            /* we did not find an optional indefinite string, so we 
00855             * don't encode it.  However, if TakeFromBuf is on, we stop 
00856             * here anyway to give our caller a chance to intercept at the 
00857             * same point where we would stop if the field were present. 
00858             */
00859            state->top->status = needBytes;
00860        }
00861        return;
00862     }
00863 
00864     if (indefinite) {
00865        /*
00866         * We need to put out an indefinite-length encoding.
00867         * The only universal types that can be constructed are SETs,
00868         * SEQUENCEs, and strings; so check that it is one of those,
00869         * or that it is not universal (e.g. context-specific).
00870         */
00871        state->indefinite = PR_TRUE;
00872        PORT_Assert ((tag_number == SEC_ASN1_SET)
00873                    || (tag_number == SEC_ASN1_SEQUENCE)
00874                    || ((tag_modifiers & SEC_ASN1_CLASS_MASK) != 0)
00875                    || state->is_string);
00876        tag_modifiers |= SEC_ASN1_CONSTRUCTED;
00877        contents_length = 0;
00878     }
00879 
00880     sec_asn1e_write_identifier_bytes (state, 
00881                                 (unsigned char)(tag_number | tag_modifiers));
00882     sec_asn1e_write_length_bytes (state, contents_length, state->indefinite);
00883 
00884     if (contents_length == 0 && !state->indefinite) {
00885        /*
00886         * If no real contents to encode, then we are done with this field.
00887         */
00888        state->place = afterContents;
00889        return;
00890     }
00891 
00892     /*
00893      * An EXPLICIT is nothing but an outer header, which we have already
00894      * written.  Now we need to do the inner header and contents.
00895      */
00896     if (state->isExplicit) {
00897        const SEC_ASN1Template *subt =
00898              SEC_ASN1GetSubtemplate(state->theTemplate, state->src, PR_TRUE);
00899        state->place = afterContents;
00900        state = sec_asn1e_push_state (state->top, subt, state->src, PR_TRUE);
00901        if (state != NULL)
00902            state = sec_asn1e_init_state_based_on_template (state);
00903        return;
00904     }
00905 
00906     switch (state->underlying_kind) {
00907       case SEC_ASN1_SET_OF:
00908       case SEC_ASN1_SEQUENCE_OF:
00909        /*
00910         * We need to push a child to handle each member.
00911         */
00912        {
00913            void **group;
00914            const SEC_ASN1Template *subt;
00915 
00916            group = *(void ***)state->src;
00917            if (group == NULL || *group == NULL) {
00918               /*
00919                * Group is empty; we are done.
00920                */
00921               state->place = afterContents;
00922               return;
00923            }
00924            state->place = duringGroup;
00925            subt = SEC_ASN1GetSubtemplate (state->theTemplate, state->src,
00926                                       PR_TRUE);
00927            state = sec_asn1e_push_state (state->top, subt, *group, PR_TRUE);
00928            if (state != NULL)
00929               state = sec_asn1e_init_state_based_on_template (state);
00930        }
00931        break;
00932 
00933       case SEC_ASN1_SEQUENCE:
00934       case SEC_ASN1_SET:
00935        /*
00936         * We need to push a child to handle the individual fields.
00937         */
00938        state->place = duringSequence;
00939        state = sec_asn1e_push_state (state->top, state->theTemplate + 1,
00940                                   state->src, PR_TRUE);
00941        if (state != NULL) {
00942            /*
00943             * Do the "before" field notification.
00944             */
00945            sec_asn1e_notify_before (state->top, state->src, state->depth);
00946            state = sec_asn1e_init_state_based_on_template (state);
00947        }
00948        break;
00949 
00950       default:
00951        /*
00952         * I think we do not need to do anything else.
00953         * XXX Correct?
00954         */
00955        state->place = duringContents;
00956        break;
00957     }
00958 }
00959 
00960 
00961 static void
00962 sec_asn1e_write_contents_from_buf (sec_asn1e_state *state,
00963                        const char *buf, unsigned long len)
00964 {
00965     PORT_Assert (state->place == duringContents);
00966     PORT_Assert (state->top->from_buf);
00967     PORT_Assert (state->may_stream && !state->disallowStreaming);
00968 
00969     /*
00970      * Probably they just turned on "take from buf", but have not
00971      * yet given us any bytes.  If there is nothing in the buffer
00972      * then we have nothing to do but return and wait.
00973      */
00974     if (buf == NULL || len == 0) {
00975        state->top->status = needBytes;
00976        return;
00977     }
00978     /*
00979      * We are streaming, reading from a passed-in buffer.
00980      * This means we are encoding a simple string or an ANY.
00981      * For the former, we need to put out a substring, with its
00982      * own identifier and length.  For an ANY, we just write it
00983      * out as is (our caller is required to ensure that it
00984      * is a properly encoded entity).
00985      */
00986     PORT_Assert (state->is_string);              /* includes ANY */
00987     if (state->underlying_kind != SEC_ASN1_ANY) {
00988        unsigned char identifier;
00989 
00990        /*
00991         * Create the identifier based on underlying_kind.  We cannot
00992         * use tag_number and tag_modifiers because this can be an
00993         * implicitly encoded field.  In that case, the underlying
00994         * substrings *are* encoded with their real tag.
00995         */
00996        identifier = (unsigned char)
00997                            (state->underlying_kind & SEC_ASN1_TAG_MASK);
00998        /*
00999         * The underlying kind should just be a simple string; there
01000         * should be no bits like CONTEXT_SPECIFIC or CONSTRUCTED set.
01001         */
01002        PORT_Assert ((identifier & SEC_ASN1_TAGNUM_MASK) == identifier);
01003        /*
01004         * Write out the tag and length for the substring.
01005         */
01006        sec_asn1e_write_identifier_bytes (state, identifier);
01007        if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
01008            char byte;
01009            /*
01010             * Assume we have a length in bytes but we need to output
01011             * a proper bit string.  This interface only works for bit
01012             * strings that are full multiples of 8.  If support for
01013             * real, variable length bit strings is needed then the
01014             * caller will have to know to pass in a bit length instead
01015             * of a byte length and then this code will have to
01016             * perform the encoding necessary (length written is length
01017             * in bytes plus 1, and the first octet of string is the
01018             * number of bits remaining between the end of the bit
01019             * string and the next byte boundary).
01020             */
01021            sec_asn1e_write_length_bytes (state, len + 1, PR_FALSE);
01022            byte = 0;
01023            sec_asn1e_write_contents_bytes (state, &byte, 1);
01024        } else {
01025            sec_asn1e_write_length_bytes (state, len, PR_FALSE);
01026        }
01027     }
01028     sec_asn1e_write_contents_bytes (state, buf, len);
01029     state->top->status = needBytes;
01030 }
01031 
01032 static void
01033 sec_asn1e_write_contents (sec_asn1e_state *state)
01034 {
01035     unsigned long len = 0;
01036 
01037     PORT_Assert (state->place == duringContents);
01038 
01039     switch (state->underlying_kind) {
01040       case SEC_ASN1_SET:
01041       case SEC_ASN1_SEQUENCE:
01042        PORT_Assert (0);
01043        break;
01044 
01045       case SEC_ASN1_BIT_STRING:
01046        {
01047            SECItem *item;
01048            char rem;
01049 
01050            item = (SECItem *)state->src;
01051            len = (item->len + 7) >> 3;
01052            rem = (unsigned char)((len << 3) - item->len); /* remaining bits */
01053            sec_asn1e_write_contents_bytes (state, &rem, 1);
01054            sec_asn1e_write_contents_bytes (state, (char *) item->data, len);
01055        }
01056        break;
01057 
01058       case SEC_ASN1_BMP_STRING:
01059        /* The number of bytes must be divisable by 2 */
01060        if ((((SECItem *)state->src)->len) % 2) {
01061            SEC_ASN1EncoderContext *cx;
01062 
01063            cx = state->top;
01064            cx->status = encodeError;
01065            break;
01066        }
01067        /* otherwise, fall through to write the content */
01068        goto process_string;
01069 
01070       case SEC_ASN1_UNIVERSAL_STRING:
01071        /* The number of bytes must be divisable by 4 */
01072        if ((((SECItem *)state->src)->len) % 4) {
01073            SEC_ASN1EncoderContext *cx;
01074 
01075            cx = state->top;
01076            cx->status = encodeError;
01077            break;
01078        }
01079        /* otherwise, fall through to write the content */
01080        goto process_string;
01081 
01082       case SEC_ASN1_INTEGER:
01083        /* ASN.1 INTEGERs are signed.  If the source is an unsigned
01084        * integer, the encoder will need to handle the conversion here.
01085        */
01086        {
01087            unsigned int blen;
01088            unsigned char *buf;
01089            SECItemType integerType;
01090            blen = ((SECItem *)state->src)->len;
01091            buf = ((SECItem *)state->src)->data;
01092            integerType = ((SECItem *)state->src)->type;
01093            while (blen > 0) {
01094               if (*buf & 0x80 && integerType == siUnsignedInteger) {
01095                   char zero = 0; /* write a leading 0 */
01096                   sec_asn1e_write_contents_bytes(state, &zero, 1);
01097                   /* and then the remaining buffer */
01098                   sec_asn1e_write_contents_bytes(state, 
01099                                              (char *)buf, blen); 
01100                   break;
01101               } 
01102               /* Check three possibilities:
01103                * 1.  No leading zeros, msb of MSB is not 1;
01104                * 2.  The number is zero itself;
01105                * 3.  Encoding a signed integer with a leading zero,
01106                *     keep the zero so that the number is positive.
01107                */
01108               if (*buf != 0 || 
01109                    blen == 1 || 
01110                    (buf[1] & 0x80 && integerType != siUnsignedInteger) ) 
01111               {
01112                   sec_asn1e_write_contents_bytes(state, 
01113                                              (char *)buf, blen); 
01114                   break;
01115               }
01116               /* byte is 0, continue */
01117               buf++;
01118               blen--;
01119            }
01120        }
01121        /* done with this content */
01122        break;
01123                      
01124 process_string:                    
01125       default:
01126        {
01127            SECItem *item;
01128 
01129            item = (SECItem *)state->src;
01130            sec_asn1e_write_contents_bytes (state, (char *) item->data,
01131                                        item->len);
01132        }
01133        break;
01134     }
01135     state->place = afterContents;
01136 }
01137 
01138 /*
01139  * We are doing a SET OF or SEQUENCE OF, and have just finished an item.
01140  */
01141 static void
01142 sec_asn1e_next_in_group (sec_asn1e_state *state)
01143 {
01144     sec_asn1e_state *child;
01145     void **group;
01146     void *member;
01147 
01148     PORT_Assert (state->place == duringGroup);
01149     PORT_Assert (state->child != NULL);
01150 
01151     child = state->child;
01152 
01153     group = *(void ***)state->src;
01154 
01155     /*
01156      * Find placement of current item.
01157      */
01158     member = (char *)(state->child->src) - child->theTemplate->offset;
01159     while (*group != member)
01160        group++;
01161 
01162     /*
01163      * Move forward to next item.
01164      */
01165     group++;
01166     if (*group == NULL) {
01167        /*
01168         * That was our last one; we are done now.
01169         */
01170        child->place = notInUse;
01171        state->place = afterContents;
01172        return;
01173     }
01174     child->src = (char *)(*group) + child->theTemplate->offset;
01175 
01176     /*
01177      * Re-"push" child.
01178      */
01179     sec_asn1e_scrub_state (child);
01180     state->top->current = child;
01181 }
01182 
01183 
01184 /*
01185  * We are moving along through a sequence; move forward by one,
01186  * (detecting end-of-sequence when it happens).
01187  */
01188 static void
01189 sec_asn1e_next_in_sequence (sec_asn1e_state *state)
01190 {
01191     sec_asn1e_state *child;
01192 
01193     PORT_Assert (state->place == duringSequence);
01194     PORT_Assert (state->child != NULL);
01195 
01196     child = state->child;
01197 
01198     /*
01199      * Do the "after" field notification.
01200      */
01201     sec_asn1e_notify_after (state->top, child->src, child->depth);
01202 
01203     /*
01204      * Move forward.
01205      */
01206     child->theTemplate++;
01207     if (child->theTemplate->kind == 0) {
01208        /*
01209         * We are done with this sequence.
01210         */
01211        child->place = notInUse;
01212        state->place = afterContents;
01213        return;
01214     }
01215 
01216     /*
01217      * Reset state and push.
01218      */
01219 
01220     child->src = (char *)state->src + child->theTemplate->offset;
01221 
01222     /*
01223      * Do the "before" field notification.
01224      */
01225     sec_asn1e_notify_before (state->top, child->src, child->depth);
01226 
01227     state->top->current = child;
01228     (void) sec_asn1e_init_state_based_on_template (child);
01229 }
01230 
01231 
01232 static void
01233 sec_asn1e_after_contents (sec_asn1e_state *state)
01234 {
01235     PORT_Assert (state->place == afterContents);
01236 
01237     if (state->indefinite)
01238        sec_asn1e_write_end_of_contents_bytes (state);
01239 
01240     /*
01241      * Just make my parent be the current state.  It will then clean
01242      * up after me and free me (or reuse me).
01243      */
01244     state->top->current = state->parent;
01245 }
01246 
01247 
01248 /*
01249  * This function is called whether or not we are streaming; if we
01250  * *are* streaming, our caller can also instruct us to take bytes
01251  * from the passed-in buffer (at buf, for length len, which is likely
01252  * bytes but could even mean bits if the current field is a bit string).
01253  * If we have been so instructed, we will gobble up bytes from there
01254  * (rather than from our src structure) and output them, and then
01255  * we will just return, expecting to be called again -- either with
01256  * more bytes or after our caller has instructed us that we are done
01257  * (for now) with the buffer.
01258  */
01259 SECStatus
01260 SEC_ASN1EncoderUpdate (SEC_ASN1EncoderContext *cx,
01261                      const char *buf, unsigned long len)
01262 {
01263     sec_asn1e_state *state;
01264 
01265     if (cx->status == needBytes) {
01266        cx->status = keepGoing;
01267     }
01268 
01269     while (cx->status == keepGoing) {
01270        state = cx->current;
01271        switch (state->place) {
01272          case beforeHeader:
01273            sec_asn1e_write_header (state);
01274            break;
01275          case duringContents:
01276            if (cx->from_buf)
01277               sec_asn1e_write_contents_from_buf (state, buf, len);
01278            else
01279               sec_asn1e_write_contents (state);
01280            break;
01281          case duringGroup:
01282            sec_asn1e_next_in_group (state);
01283            break;
01284          case duringSequence:
01285            sec_asn1e_next_in_sequence (state);
01286            break;
01287          case afterContents:
01288            sec_asn1e_after_contents (state);
01289            break;
01290          case afterImplicit:
01291          case afterInline:
01292          case afterPointer:
01293          case afterChoice:
01294            /*
01295             * These states are more documentation than anything.
01296             * They just need to force a pop.
01297             */
01298            PORT_Assert (!state->indefinite);
01299            state->place = afterContents;
01300            break;
01301          case notInUse:
01302          default:
01303            /* This is not an error, but rather a plain old BUG! */
01304            PORT_Assert (0);
01305            cx->status = encodeError;
01306            break;
01307        }
01308 
01309        if (cx->status == encodeError)
01310            break;
01311 
01312        /* It might have changed, so we have to update our local copy.  */
01313        state = cx->current;
01314 
01315        /* If it is NULL, we have popped all the way to the top.  */
01316        if (state == NULL) {
01317            cx->status = allDone;
01318            break;
01319        }
01320     }
01321 
01322     if (cx->status == encodeError) {
01323        return SECFailure;
01324     }
01325 
01326     return SECSuccess;
01327 }
01328 
01329 
01330 void
01331 SEC_ASN1EncoderFinish (SEC_ASN1EncoderContext *cx)
01332 {
01333     /*
01334      * XXX anything else that needs to be finished?
01335      */
01336 
01337     PORT_FreeArena (cx->our_pool, PR_FALSE);
01338 }
01339 
01340 
01341 SEC_ASN1EncoderContext *
01342 SEC_ASN1EncoderStart (const void *src, const SEC_ASN1Template *theTemplate,
01343                     SEC_ASN1WriteProc output_proc, void *output_arg)
01344 {
01345     PRArenaPool *our_pool;
01346     SEC_ASN1EncoderContext *cx;
01347 
01348     our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE);
01349     if (our_pool == NULL)
01350        return NULL;
01351 
01352     cx = (SEC_ASN1EncoderContext*)PORT_ArenaZAlloc (our_pool, sizeof(*cx));
01353     if (cx == NULL) {
01354        PORT_FreeArena (our_pool, PR_FALSE);
01355        return NULL;
01356     }
01357 
01358     cx->our_pool = our_pool;
01359     cx->output_proc = output_proc;
01360     cx->output_arg = output_arg;
01361 
01362     cx->status = keepGoing;
01363 
01364     if (sec_asn1e_push_state(cx, theTemplate, src, PR_FALSE) == NULL
01365        || sec_asn1e_init_state_based_on_template (cx->current) == NULL) {
01366        /*
01367         * Trouble initializing (probably due to failed allocations)
01368         * requires that we just give up.
01369         */
01370        PORT_FreeArena (our_pool, PR_FALSE);
01371        return NULL;
01372     }
01373 
01374     return cx;
01375 }
01376 
01377 
01378 /*
01379  * XXX Do we need a FilterProc, too?
01380  */
01381 
01382 
01383 void
01384 SEC_ASN1EncoderSetNotifyProc (SEC_ASN1EncoderContext *cx,
01385                            SEC_ASN1NotifyProc fn, void *arg)
01386 {
01387     cx->notify_proc = fn;
01388     cx->notify_arg = arg;
01389 }
01390 
01391 
01392 void
01393 SEC_ASN1EncoderClearNotifyProc (SEC_ASN1EncoderContext *cx)
01394 {
01395     cx->notify_proc = NULL;
01396     cx->notify_arg = NULL;  /* not necessary; just being clean */
01397 }
01398 
01399 void
01400 SEC_ASN1EncoderAbort(SEC_ASN1EncoderContext *cx, int error)
01401 {
01402     PORT_Assert(cx);
01403     PORT_SetError(error);
01404     cx->status = encodeError;
01405 }
01406 
01407 void
01408 SEC_ASN1EncoderSetStreaming (SEC_ASN1EncoderContext *cx)
01409 {
01410     /* XXX is there a way to check that we are "between" fields here? */
01411 
01412     cx->streaming = PR_TRUE;
01413 }
01414 
01415 
01416 void
01417 SEC_ASN1EncoderClearStreaming (SEC_ASN1EncoderContext *cx)
01418 {
01419     /* XXX is there a way to check that we are "between" fields here? */
01420 
01421     cx->streaming = PR_FALSE;
01422 }
01423 
01424 
01425 void
01426 SEC_ASN1EncoderSetTakeFromBuf (SEC_ASN1EncoderContext *cx)
01427 {
01428     /* 
01429      * XXX is there a way to check that we are "between" fields here?  this
01430      * needs to include a check for being in between groups of items in
01431      * a SET_OF or SEQUENCE_OF.
01432      */
01433     PORT_Assert (cx->streaming);
01434 
01435     cx->from_buf = PR_TRUE;
01436 }
01437 
01438 
01439 void
01440 SEC_ASN1EncoderClearTakeFromBuf (SEC_ASN1EncoderContext *cx)
01441 {
01442     /* we should actually be taking from buf *now* */
01443     PORT_Assert (cx->from_buf);
01444     if (! cx->from_buf)            /* if not, just do nothing */
01445        return;
01446 
01447     cx->from_buf = PR_FALSE;
01448 
01449     if (cx->status == needBytes) {
01450        cx->status = keepGoing;
01451        cx->current->place = afterContents;
01452     }
01453 }
01454 
01455 
01456 SECStatus
01457 SEC_ASN1Encode (const void *src, const SEC_ASN1Template *theTemplate,
01458               SEC_ASN1WriteProc output_proc, void *output_arg)
01459 {
01460     SEC_ASN1EncoderContext *ecx;
01461     SECStatus rv;
01462 
01463     ecx = SEC_ASN1EncoderStart (src, theTemplate, output_proc, output_arg);
01464     if (ecx == NULL)
01465        return SECFailure;
01466 
01467     rv = SEC_ASN1EncoderUpdate (ecx, NULL, 0);
01468 
01469     SEC_ASN1EncoderFinish (ecx);
01470     return rv;
01471 }
01472 
01473 
01474 /*
01475  * XXX depth and data_kind are unused; is there a PC way to silence warnings?
01476  * (I mean "politically correct", not anything to do with intel/win platform) 
01477  */
01478 static void
01479 sec_asn1e_encode_item_count (void *arg, const char *buf, unsigned long len,
01480                           int depth, SEC_ASN1EncodingPart data_kind)
01481 {
01482     unsigned long *count;
01483 
01484     count = (unsigned long*)arg;
01485     PORT_Assert (count != NULL);
01486 
01487     *count += len;
01488 }
01489 
01490 
01491 /* XXX depth and data_kind are unused; is there a PC way to silence warnings? */
01492 static void
01493 sec_asn1e_encode_item_store (void *arg, const char *buf, unsigned long len,
01494                           int depth, SEC_ASN1EncodingPart data_kind)
01495 {
01496     SECItem *dest;
01497 
01498     dest = (SECItem*)arg;
01499     PORT_Assert (dest != NULL);
01500 
01501     PORT_Memcpy (dest->data + dest->len, buf, len);
01502     dest->len += len;
01503 }
01504 
01505 
01506 /*
01507  * Allocate an entire SECItem, or just the data part of it, to hold
01508  * "len" bytes of stuff.  Allocate from the given pool, if specified,
01509  * otherwise just do a vanilla PORT_Alloc.
01510  *
01511  * XXX This seems like a reasonable general-purpose function (for SECITEM_)?
01512  */
01513 static SECItem *
01514 sec_asn1e_allocate_item (PRArenaPool *poolp, SECItem *dest, unsigned long len)
01515 {
01516     if (poolp != NULL) {
01517        void *release;
01518 
01519        release = PORT_ArenaMark (poolp);
01520        if (dest == NULL)
01521            dest = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem));
01522        if (dest != NULL) {
01523            dest->data = (unsigned char*)PORT_ArenaAlloc (poolp, len);
01524            if (dest->data == NULL) {
01525               dest = NULL;
01526            }
01527        }
01528        if (dest == NULL) {
01529            /* one or both allocations failed; release everything */
01530            PORT_ArenaRelease (poolp, release);
01531        } else {
01532            /* everything okay; unmark the arena */
01533            PORT_ArenaUnmark (poolp, release);
01534        }
01535     } else {
01536        SECItem *indest;
01537 
01538        indest = dest;
01539        if (dest == NULL)
01540            dest = (SECItem*)PORT_Alloc (sizeof(SECItem));
01541        if (dest != NULL) {
01542            dest->type = siBuffer;
01543            dest->data = (unsigned char*)PORT_Alloc (len);
01544            if (dest->data == NULL) {
01545               if (indest == NULL)
01546                   PORT_Free (dest);
01547               dest = NULL;
01548            }
01549        }
01550     }
01551 
01552     return dest;
01553 }
01554 
01555 
01556 SECItem *
01557 SEC_ASN1EncodeItem (PRArenaPool *poolp, SECItem *dest, const void *src,
01558                   const SEC_ASN1Template *theTemplate)
01559 {
01560     unsigned long encoding_length;
01561     SECStatus rv;
01562 
01563     PORT_Assert (dest == NULL || dest->data == NULL);
01564 
01565     encoding_length = 0;
01566     rv = SEC_ASN1Encode (src, theTemplate,
01567                       sec_asn1e_encode_item_count, &encoding_length);
01568     if (rv != SECSuccess)
01569        return NULL;
01570 
01571     dest = sec_asn1e_allocate_item (poolp, dest, encoding_length);
01572     if (dest == NULL)
01573        return NULL;
01574 
01575     /* XXX necessary?  This really just checks for a bug in the allocate fn */
01576     PORT_Assert (dest->data != NULL);
01577     if (dest->data == NULL)
01578        return NULL;
01579 
01580     dest->len = 0;
01581     (void) SEC_ASN1Encode (src, theTemplate, sec_asn1e_encode_item_store, dest);
01582 
01583     PORT_Assert (encoding_length == dest->len);
01584     return dest;
01585 }
01586 
01587 
01588 static SECItem *
01589 sec_asn1e_integer(PRArenaPool *poolp, SECItem *dest, unsigned long value,
01590                 PRBool make_unsigned)
01591 {
01592     unsigned long copy;
01593     unsigned char sign;
01594     int len = 0;
01595 
01596     /*
01597      * Determine the length of the encoded value (minimum of 1).
01598      */
01599     copy = value;
01600     do {
01601        len++;
01602        sign = (unsigned char)(copy & 0x80);
01603        copy >>= 8;
01604     } while (copy);
01605 
01606     /*
01607      * If this is an unsigned encoding, and the high bit of the last
01608      * byte we counted was set, we need to add one to the length so
01609      * we put a high-order zero byte in the encoding.
01610      */
01611     if (sign && make_unsigned)
01612        len++;
01613 
01614     /*
01615      * Allocate the item (if necessary) and the data pointer within.
01616      */
01617     dest = sec_asn1e_allocate_item (poolp, dest, len);
01618     if (dest == NULL)
01619        return NULL;
01620 
01621     /*
01622      * Store the value, byte by byte, in the item.
01623      */
01624     dest->len = len;
01625     while (len) {
01626        dest->data[--len] = (unsigned char)value;
01627        value >>= 8;
01628     }
01629     PORT_Assert (value == 0);
01630 
01631     return dest;
01632 }
01633 
01634 
01635 SECItem *
01636 SEC_ASN1EncodeInteger(PRArenaPool *poolp, SECItem *dest, long value)
01637 {
01638     return sec_asn1e_integer (poolp, dest, (unsigned long) value, PR_FALSE);
01639 }
01640 
01641 
01642 SECItem *
01643 SEC_ASN1EncodeUnsignedInteger(PRArenaPool *poolp,
01644                            SECItem *dest, unsigned long value)
01645 {
01646     return sec_asn1e_integer (poolp, dest, value, PR_TRUE);
01647 }