dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libkmf / ber_der / common / encode.c
blob73fe1b0ddf952dbcdf81c43ce3c86f238f0a95ee
1 /*
2 * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
4 * The contents of this file are subject to the Netscape Public License
5 * Version 1.0 (the "NPL"); you may not use this file except in
6 * compliance with the NPL. You may obtain a copy of the NPL at
7 * http://www.mozilla.org/NPL/
9 * Software distributed under the NPL is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
11 * for the specific language governing rights and limitations under the
12 * NPL.
14 * The Initial Developer of this code under the NPL is Netscape
15 * Communications Corporation. Portions created by Netscape are
16 * Copyright (C) 1998 Netscape Communications Corporation. All Rights
17 * Reserved.
21 * Copyright (c) 1990 Regents of the University of Michigan.
22 * All rights reserved.
24 * Redistribution and use in source and binary forms are permitted
25 * provided that this notice is preserved and that due credit is given
26 * to the University of Michigan at Ann Arbor. The name of the University
27 * may not be used to endorse or promote products derived from this
28 * software without specific prior written permission. This software
29 * is provided ``as is'' without express or implied warranty.
33 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
34 * Use is subject to license terms.
37 #include <sys/types.h>
38 #include <netinet/in.h>
39 #include <inttypes.h>
41 #include <ber_der.h>
42 #include "kmfber_int.h"
44 /* the following constants are used in kmfber_calc_lenlen */
46 #define LENMASK1 0xFF
47 #define LENMASK2 0xFFFF
48 #define LENMASK3 0xFFFFFF
49 #define LENMASK4 0xFFFFFFFF
50 #define _MASK 0x80
52 int
53 kmfber_calc_taglen(ber_tag_t tag)
55 int i;
56 ber_int_t mask;
58 /* find the first non-all-zero byte in the tag */
59 for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
60 mask = (LENMASK3 << (i * 8));
61 /* not all zero */
62 if (tag & mask)
63 break;
66 return (i + 1);
69 static int
70 ber_put_tag(BerElement *ber, ber_tag_t tag, int nosos)
72 ber_int_t taglen;
73 ber_tag_t ntag;
75 taglen = kmfber_calc_taglen(tag);
77 ntag = htonl(tag);
79 return (kmfber_write(ber,
80 ((char *) &ntag) + sizeof (ber_int_t) - taglen,
81 taglen, nosos));
84 int
85 kmfber_calc_lenlen(ber_int_t len)
88 * short len if it's less than 128 - one byte giving the len,
89 * with bit 8 0.
92 if (len <= 0x7F)
93 return (1);
96 * long len otherwise - one byte with bit 8 set, giving the
97 * length of the length, followed by the length itself.
100 if (len <= LENMASK1)
101 return (2);
102 if (len <= LENMASK2)
103 return (3);
104 if (len <= LENMASK3)
105 return (4);
107 return (5);
111 kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
113 int i;
114 char lenlen;
115 ber_int_t mask, netlen;
118 * short len if it's less than 128 - one byte giving the len,
119 * with bit 8 0.
121 if (len <= 127) {
122 netlen = htonl(len);
123 return (kmfber_write(ber,
124 (char *)&netlen + sizeof (ber_int_t) - 1,
125 1, nosos));
129 * long len otherwise - one byte with bit 8 set, giving the
130 * length of the length, followed by the length itself.
133 /* find the first non-all-zero byte */
134 for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
135 mask = (LENMASK1 << (i * 8));
136 /* not all zero */
137 if (len & mask)
138 break;
140 lenlen = ++i;
141 if (lenlen > 4)
142 return (-1);
143 lenlen |= 0x80;
145 /* write the length of the length */
146 if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
147 return (-1);
149 /* write the length itself */
150 netlen = htonl(len);
151 if (kmfber_write(ber,
152 (char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
153 return (-1);
155 return (i + 1);
158 static int
159 ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
161 int i, sign;
162 ber_int_t len, lenlen, taglen, netnum, mask;
164 sign = (num < 0);
167 * high bit is set - look for first non-all-one byte
168 * high bit is clear - look for first non-all-zero byte
170 for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
171 mask = (LENMASK1 << (i * 8));
173 if (sign) {
174 /* not all ones */
175 if ((num & mask) != mask)
176 break;
177 } else {
178 /* not all zero */
179 if (num & mask)
180 break;
185 * we now have the "leading byte". if the high bit on this
186 * byte matches the sign bit, we need to "back up" a byte.
188 mask = (num & (_MASK << (i * 8)));
189 if ((mask && !sign) || (sign && !mask))
190 i++;
192 len = i + 1;
194 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
195 return (-1);
197 if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
198 return (-1);
199 i++;
200 netnum = htonl(num);
201 if (kmfber_write(ber,
202 (char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
203 /* length of tag + length + contents */
204 return (taglen + lenlen + i);
206 return (-1);
209 static int
210 kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
212 if (tag == KMFBER_DEFAULT)
213 tag = BER_ENUMERATED;
215 return (ber_put_int_or_enum(ber, num, tag));
219 ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
221 if (tag == KMFBER_DEFAULT)
222 tag = BER_INTEGER;
224 return (ber_put_int_or_enum(ber, num, tag));
228 ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
230 ber_int_t taglen, lenlen, rc, len;
232 if (tag == KMFBER_DEFAULT)
233 tag = 0x06; /* TODO: Add new OID constant to header */
235 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
236 return (-1);
238 len = (ber_int_t)oid->bv_len;
239 if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
240 kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
241 (ber_int_t)oid->bv_len) {
242 rc = -1;
243 } else {
244 /* return length of tag + length + contents */
245 rc = taglen + lenlen + oid->bv_len;
247 return (rc);
251 ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
252 ber_len_t len)
254 ber_int_t taglen, lenlen, ilen, rc;
255 char zero = 0x00;
257 if (tag == KMFBER_DEFAULT)
258 tag = BER_INTEGER;
260 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
261 return (-1);
263 /* Add a leading 0 if the high order bit is set */
264 if (data[0] & 0x80)
265 len++;
267 ilen = (ber_int_t)len;
268 if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1)
269 return (-1);
271 /* add leading 0 if hi bit set */
272 if ((data[0] & 0x80) && kmfber_write(ber, &zero, 1, 0) != 1)
273 return (-1);
275 /* Adjust the length of the write if hi-order bit is set */
276 if (data[0] & 0x80)
277 ilen = len - 1;
278 if (kmfber_write(ber, data, ilen, 0) != (ber_int_t)ilen) {
279 return (-1);
280 } else {
281 /* return length of tag + length + contents */
282 rc = taglen + lenlen + len;
284 return (rc);
287 static int
288 kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
289 ber_tag_t tag)
291 ber_int_t taglen, lenlen, ilen, rc;
292 #ifdef STR_TRANSLATION
293 int free_str;
294 #endif /* STR_TRANSLATION */
296 if (tag == KMFBER_DEFAULT)
297 tag = BER_OCTET_STRING;
299 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
300 return (-1);
302 #ifdef STR_TRANSLATION
303 if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
304 ber->ber_encode_translate_proc != NULL) {
305 if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
306 != 0) {
307 return (-1);
309 free_str = 1;
310 } else {
311 free_str = 0;
313 #endif /* STR_TRANSLATION */
316 * Note: below is a spot where we limit ber_write
317 * to signed long (instead of unsigned long)
319 ilen = (ber_int_t)len;
320 if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
321 kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
322 rc = -1;
323 } else {
324 /* return length of tag + length + contents */
325 rc = taglen + lenlen + len;
328 #ifdef STR_TRANSLATION
329 if (free_str) {
330 free(str);
332 #endif /* STR_TRANSLATION */
334 return (rc);
337 static int
338 kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
340 return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
343 static int
344 kmfber_put_bitstring(BerElement *ber, char *str,
345 ber_len_t blen /* in bits */, ber_tag_t tag)
347 ber_int_t taglen, lenlen, len;
348 unsigned char unusedbits;
350 if (tag == KMFBER_DEFAULT)
351 tag = BER_BIT_STRING;
353 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
354 return (-1);
356 len = (blen + 7) / 8;
357 unusedbits = (unsigned char) (len * 8 - blen);
358 if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
359 return (-1);
361 if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
362 return (-1);
364 if (kmfber_write(ber, str, len, 0) != len)
365 return (-1);
367 /* return length of tag + length + unused bit count + contents */
368 return (taglen + 1 + lenlen + len);
371 static int
372 kmfber_put_null(BerElement *ber, ber_tag_t tag)
374 int taglen;
376 if (tag == KMFBER_DEFAULT)
377 tag = BER_NULL;
379 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
380 return (-1);
382 if (kmfber_put_len(ber, 0, 0) != 1)
383 return (-1);
385 return (taglen + 1);
388 static int
389 kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
391 int taglen;
392 unsigned char trueval = 0xff;
393 unsigned char falseval = 0x00;
395 if (tag == KMFBER_DEFAULT)
396 tag = BER_BOOLEAN;
398 if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
399 return (-1);
401 if (kmfber_put_len(ber, 1, 0) != 1)
402 return (-1);
404 if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
405 != 1)
406 return (-1);
408 return (taglen + 2);
411 #define FOUR_BYTE_LEN 5
415 * The idea here is roughly this: we maintain a stack of these Seqorset
416 * structures. This is pushed when we see the beginning of a new set or
417 * sequence. It is popped when we see the end of a set or sequence.
418 * Since we don't want to malloc and free these structures all the time,
419 * we pre-allocate a small set of them within the ber element structure.
420 * thus we need to spot when we've overflowed this stack and fall back to
421 * malloc'ing instead.
423 static int
424 ber_start_seqorset(BerElement *ber, ber_tag_t tag)
426 Seqorset *new_sos;
428 /* can we fit into the local stack ? */
429 if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
430 /* yes */
431 new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
432 } else {
433 /* no */
434 if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
435 == NULLSEQORSET) {
436 return (-1);
439 ber->ber_sos_stack_posn++;
441 if (ber->ber_sos == NULLSEQORSET)
442 new_sos->sos_first = ber->ber_ptr;
443 else
444 new_sos->sos_first = ber->ber_sos->sos_ptr;
446 /* Set aside room for a 4 byte length field */
447 new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
448 FOUR_BYTE_LEN;
449 new_sos->sos_tag = tag;
451 new_sos->sos_next = ber->ber_sos;
452 new_sos->sos_clen = 0;
454 ber->ber_sos = new_sos;
455 if (ber->ber_sos->sos_ptr > ber->ber_end) {
456 if (kmfber_realloc(ber, ber->ber_sos->sos_ptr -
457 ber->ber_end) != 0)
458 return (-1);
460 return (0);
463 static int
464 kmfber_start_seq(BerElement *ber, ber_tag_t tag)
466 if (tag == KMFBER_DEFAULT)
467 tag = BER_CONSTRUCTED_SEQUENCE;
469 return (ber_start_seqorset(ber, tag));
472 static int
473 kmfber_start_set(BerElement *ber, ber_tag_t tag)
475 if (tag == KMFBER_DEFAULT)
476 tag = BER_CONSTRUCTED_SET;
478 return (ber_start_seqorset(ber, tag));
481 static int
482 ber_put_seqorset(BerElement *ber)
484 ber_int_t netlen, len, taglen, lenlen;
485 unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1;
486 Seqorset *next;
487 Seqorset **sos = &ber->ber_sos;
490 * If this is the toplevel sequence or set, we need to actually
491 * write the stuff out. Otherwise, it's already been put in
492 * the appropriate buffer and will be written when the toplevel
493 * one is written. In this case all we need to do is update the
494 * length and tag.
497 len = (*sos)->sos_clen;
498 netlen = (ber_len_t)htonl(len);
500 if (ber->ber_options & KMFBER_OPT_USE_DER) {
501 lenlen = kmfber_calc_lenlen(len);
502 } else {
503 lenlen = FOUR_BYTE_LEN;
506 if ((next = (*sos)->sos_next) == NULLSEQORSET) {
507 /* write the tag */
508 if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
509 return (-1);
511 if (ber->ber_options & KMFBER_OPT_USE_DER) {
512 /* Write the length in the minimum # of octets */
513 if (kmfber_put_len(ber, len, 1) == -1)
514 return (-1);
516 if (lenlen != FOUR_BYTE_LEN) {
518 * We set aside FOUR_BYTE_LEN bytes for
519 * the length field. Move the data if
520 * we don't actually need that much
522 (void) memmove((*sos)->sos_first + taglen +
523 lenlen, (*sos)->sos_first + taglen +
524 FOUR_BYTE_LEN, len);
526 } else {
527 /* Fill FOUR_BYTE_LEN bytes for length field */
528 /* one byte of length length */
529 if (kmfber_write(ber, (char *)&ltag, 1, 1) != 1)
530 return (-1);
532 /* the length itself */
533 if (kmfber_write(ber,
534 (char *)&netlen + sizeof (ber_int_t)
535 - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
536 FOUR_BYTE_LEN - 1)
537 return (-1);
539 /* The ber_ptr is at the set/seq start - move it to the end */
540 ber->ber_ptr += len;
541 } else {
542 ber_tag_t ntag;
544 /* the tag */
545 taglen = kmfber_calc_taglen((*sos)->sos_tag);
546 ntag = htonl((*sos)->sos_tag);
547 (void) memmove((*sos)->sos_first, (char *)&ntag +
548 sizeof (ber_int_t) - taglen, taglen);
550 if (ber->ber_options & KMFBER_OPT_USE_DER) {
551 ltag = (lenlen == 1) ? (unsigned char)len :
552 (unsigned char) (0x80 + (lenlen - 1));
555 /* one byte of length length */
556 (void) memmove((*sos)->sos_first + 1, &ltag, 1);
558 if (ber->ber_options & KMFBER_OPT_USE_DER) {
559 if (lenlen > 1) {
560 /* Write the length itself */
561 (void) memmove((*sos)->sos_first + 2,
562 (char *)&netlen + sizeof (ber_uint_t) -
563 (lenlen - 1),
564 lenlen - 1);
566 if (lenlen != FOUR_BYTE_LEN) {
568 * We set aside FOUR_BYTE_LEN bytes for
569 * the length field. Move the data if
570 * we don't actually need that much
572 (void) memmove((*sos)->sos_first + taglen +
573 lenlen, (*sos)->sos_first + taglen +
574 FOUR_BYTE_LEN, len);
576 } else {
577 /* the length itself */
578 (void) memmove((*sos)->sos_first + taglen + 1,
579 (char *) &netlen + sizeof (ber_int_t) -
580 (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
583 next->sos_clen += (taglen + lenlen + len);
584 next->sos_ptr += (taglen + lenlen + len);
587 /* we're done with this seqorset, so free it up */
588 /* was this one from the local stack ? */
589 if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
590 free((char *)(*sos));
592 ber->ber_sos_stack_posn--;
593 *sos = next;
595 return (taglen + lenlen + len);
598 /* VARARGS */
600 kmfber_printf(BerElement *ber, const char *fmt, ...)
602 va_list ap;
603 char *s, **ss;
604 struct berval **bv, *oid;
605 int rc, i, t;
606 ber_int_t len;
608 va_start(ap, fmt);
610 #ifdef KMFBER_DEBUG
611 if (lber_debug & 64) {
612 char msg[80];
613 sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
614 ber_err_print(msg);
616 #endif
618 for (rc = 0; *fmt && rc != -1; fmt++) {
619 switch (*fmt) {
620 case 'b': /* boolean */
621 i = va_arg(ap, int);
622 rc = kmfber_put_boolean(ber, i, ber->ber_tag);
623 break;
625 case 'i': /* int */
626 i = va_arg(ap, int);
627 rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
628 break;
630 case 'D': /* Object ID */
631 if ((oid = va_arg(ap, struct berval *)) == NULL)
632 break;
633 rc = ber_put_oid(ber, oid, ber->ber_tag);
634 break;
635 case 'I': /* int */
636 s = va_arg(ap, char *);
637 len = va_arg(ap, ber_int_t);
638 rc = ber_put_big_int(ber, ber->ber_tag, s, len);
639 break;
641 case 'e': /* enumeration */
642 i = va_arg(ap, int);
643 rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
644 break;
646 case 'l':
647 t = va_arg(ap, int);
648 rc = kmfber_put_len(ber, t, 0);
649 break;
650 case 'n': /* null */
651 rc = kmfber_put_null(ber, ber->ber_tag);
652 break;
654 case 'o': /* octet string (non-null terminated) */
655 s = va_arg(ap, char *);
656 len = va_arg(ap, int);
657 rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
658 break;
660 case 's': /* string */
661 s = va_arg(ap, char *);
662 rc = kmfber_put_string(ber, s, ber->ber_tag);
663 break;
665 case 'B': /* bit string */
666 s = va_arg(ap, char *);
667 len = va_arg(ap, int); /* in bits */
668 rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
669 break;
671 case 't': /* tag for the next element */
672 ber->ber_tag = va_arg(ap, ber_tag_t);
673 ber->ber_usertag = 1;
674 break;
676 case 'T': /* Write an explicit tag, but don't change current */
677 t = va_arg(ap, int);
678 rc = ber_put_tag(ber, t, 0);
679 break;
681 case 'v': /* vector of strings */
682 if ((ss = va_arg(ap, char **)) == NULL)
683 break;
684 for (i = 0; ss[i] != NULL; i++) {
685 if ((rc = kmfber_put_string(ber, ss[i],
686 ber->ber_tag)) == -1)
687 break;
689 break;
691 case 'V': /* sequences of strings + lengths */
692 if ((bv = va_arg(ap, struct berval **)) == NULL)
693 break;
694 for (i = 0; bv[i] != NULL; i++) {
695 if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
696 bv[i]->bv_len, ber->ber_tag)) == -1)
697 break;
699 break;
701 case '{': /* begin sequence */
702 rc = kmfber_start_seq(ber, ber->ber_tag);
703 break;
705 case '}': /* end sequence */
706 rc = ber_put_seqorset(ber);
707 break;
709 case '[': /* begin set */
710 rc = kmfber_start_set(ber, ber->ber_tag);
711 break;
713 case ']': /* end set */
714 rc = ber_put_seqorset(ber);
715 break;
717 default: {
718 #ifdef KMFBER_DEBUG
719 char msg[80];
720 sprintf(msg, "unknown fmt %c\n", *fmt);
721 ber_err_print(msg);
722 #endif
723 rc = -1;
724 break;
728 if (ber->ber_usertag == 0)
729 ber->ber_tag = KMFBER_DEFAULT;
730 else
731 ber->ber_usertag = 0;
734 va_end(ap);
736 return (rc);