PageRenderTime 119ms CodeModel.GetById 15ms app.highlight 91ms RepoModel.GetById 2ms app.codeStats 0ms

/security/nss/lib/util/secasn1e.c

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