Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1089)

Side by Side Diff: nss/lib/util/secasn1e.c

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

Powered by Google App Engine
This is Rietveld 408576698