security/nss/lib/util/secasn1e.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial