Extract function to convert ASN.1 INTEGER object to u_int64_t
[strongswan.git] / src / libstrongswan / asn1 / asn1.c
1 /*
2 * Copyright (C) 2006 Martin Will
3 * Copyright (C) 2000-2008 Andreas Steffen
4 *
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <time.h>
21
22 #include <utils/debug.h>
23
24 #include "oid.h"
25 #include "asn1.h"
26 #include "asn1_parser.h"
27
28 /**
29 * Commonly used ASN1 values.
30 */
31 const chunk_t ASN1_INTEGER_0 = chunk_from_chars(0x02, 0x01, 0x00);
32 const chunk_t ASN1_INTEGER_1 = chunk_from_chars(0x02, 0x01, 0x01);
33 const chunk_t ASN1_INTEGER_2 = chunk_from_chars(0x02, 0x01, 0x02);
34
35 /*
36 * Defined in header.
37 */
38 chunk_t asn1_algorithmIdentifier(int oid)
39 {
40 chunk_t parameters;
41
42 /* some algorithmIdentifiers have a NULL parameters field and some do not */
43 switch (oid)
44 {
45 case OID_ECDSA_WITH_SHA1:
46 case OID_ECDSA_WITH_SHA224:
47 case OID_ECDSA_WITH_SHA256:
48 case OID_ECDSA_WITH_SHA384:
49 case OID_ECDSA_WITH_SHA512:
50 parameters = chunk_empty;
51 break;
52 default:
53 parameters = asn1_simple_object(ASN1_NULL, chunk_empty);
54 break;
55 }
56 return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(oid), parameters);
57 }
58
59 /*
60 * Defined in header.
61 */
62 int asn1_known_oid(chunk_t object)
63 {
64 int oid = 0;
65
66 while (object.len)
67 {
68 if (oid_names[oid].octet == *object.ptr)
69 {
70 if (--object.len == 0 || oid_names[oid].down == 0)
71 {
72 return oid; /* found terminal symbol */
73 }
74 else
75 {
76 object.ptr++; oid++; /* advance to next hex octet */
77 }
78 }
79 else
80 {
81 if (oid_names[oid].next)
82 {
83 oid = oid_names[oid].next;
84 }
85 else
86 {
87 return OID_UNKNOWN;
88 }
89 }
90 }
91 return -1;
92 }
93
94 /*
95 * Defined in header.
96 */
97 chunk_t asn1_build_known_oid(int n)
98 {
99 chunk_t oid;
100 int i;
101
102 if (n < 0 || n >= OID_MAX)
103 {
104 return chunk_empty;
105 }
106
107 i = oid_names[n].level + 1;
108 oid = chunk_alloc(2 + i);
109 oid.ptr[0] = ASN1_OID;
110 oid.ptr[1] = i;
111
112 do
113 {
114 if (oid_names[n].level >= i)
115 {
116 n--;
117 continue;
118 }
119 oid.ptr[--i + 2] = oid_names[n--].octet;
120 }
121 while (i > 0);
122
123 return oid;
124 }
125
126 /*
127 * Defined in header.
128 */
129 chunk_t asn1_oid_from_string(char *str)
130 {
131 enumerator_t *enumerator;
132 u_char buf[64];
133 char *end;
134 int i = 0, pos = 0, shift;
135 u_int val, shifted_val, first = 0;
136
137 enumerator = enumerator_create_token(str, ".", "");
138 while (enumerator->enumerate(enumerator, &str))
139 {
140 val = strtoul(str, &end, 10);
141 if (end == str || pos > countof(buf))
142 {
143 pos = 0;
144 break;
145 }
146 switch (i++)
147 {
148 case 0:
149 first = val;
150 break;
151 case 1:
152 buf[pos++] = first * 40 + val;
153 break;
154 default:
155 shift = 28; /* sufficient to handle 32 bit node numbers */
156 while (shift)
157 {
158 shifted_val = val >> shift;
159 shift -= 7;
160 if (shifted_val) /* do not encode leading zeroes */
161 {
162 buf[pos++] = 0x80 | (shifted_val & 0x7F);
163 }
164 }
165 buf[pos++] = val & 0x7F;
166 }
167 }
168 enumerator->destroy(enumerator);
169
170 return chunk_clone(chunk_create(buf, pos));
171 }
172
173 /*
174 * Defined in header.
175 */
176 char *asn1_oid_to_string(chunk_t oid)
177 {
178 char buf[64], *pos = buf;
179 int len;
180 u_int val;
181
182 if (!oid.len)
183 {
184 return NULL;
185 }
186 val = oid.ptr[0] / 40;
187 len = snprintf(buf, sizeof(buf), "%u.%u", val, oid.ptr[0] - val * 40);
188 oid = chunk_skip(oid, 1);
189 if (len < 0 || len >= sizeof(buf))
190 {
191 return NULL;
192 }
193 pos += len;
194 val = 0;
195
196 while (oid.len)
197 {
198 val = (val << 7) + (u_int)(oid.ptr[0] & 0x7f);
199
200 if (oid.ptr[0] < 128)
201 {
202 len = snprintf(pos, sizeof(buf) + buf - pos, ".%u", val);
203 if (len < 0 || len >= sizeof(buf) + buf - pos)
204 {
205 return NULL;
206 }
207 pos += len;
208 val = 0;
209 }
210 oid = chunk_skip(oid, 1);
211 }
212 return (val == 0) ? strdup(buf) : NULL;
213 }
214
215 /*
216 * Defined in header.
217 */
218 size_t asn1_length(chunk_t *blob)
219 {
220 u_char n;
221 size_t len;
222
223 if (blob->len < 2)
224 {
225 DBG2(DBG_ASN, "insufficient number of octets to parse ASN.1 length");
226 return ASN1_INVALID_LENGTH;
227 }
228
229 /* read length field, skip tag and length */
230 n = blob->ptr[1];
231 blob->ptr += 2;
232 blob->len -= 2;
233
234 if ((n & 0x80) == 0)
235 { /* single length octet */
236 if (n > blob->len)
237 {
238 DBG2(DBG_ASN, "length is larger than remaining blob size");
239 return ASN1_INVALID_LENGTH;
240 }
241 return n;
242 }
243
244 /* composite length, determine number of length octets */
245 n &= 0x7f;
246
247 if (n == 0 || n > blob->len)
248 {
249 DBG2(DBG_ASN, "number of length octets invalid");
250 return ASN1_INVALID_LENGTH;
251 }
252
253 if (n > sizeof(len))
254 {
255 DBG2(DBG_ASN, "number of length octets is larger than limit of"
256 " %d octets", (int)sizeof(len));
257 return ASN1_INVALID_LENGTH;
258 }
259
260 len = 0;
261
262 while (n-- > 0)
263 {
264 len = 256*len + *blob->ptr++;
265 blob->len--;
266 }
267 if (len > blob->len)
268 {
269 DBG2(DBG_ASN, "length is larger than remaining blob size");
270 return ASN1_INVALID_LENGTH;
271 }
272 return len;
273 }
274
275 /*
276 * See header.
277 */
278 int asn1_unwrap(chunk_t *blob, chunk_t *inner)
279 {
280 chunk_t res;
281 u_char len;
282 int type;
283
284 if (blob->len < 2)
285 {
286 return ASN1_INVALID;
287 }
288 type = blob->ptr[0];
289 len = blob->ptr[1];
290 *blob = chunk_skip(*blob, 2);
291
292 if ((len & 0x80) == 0)
293 { /* single length octet */
294 res.len = len;
295 }
296 else
297 { /* composite length, determine number of length octets */
298 len &= 0x7f;
299 if (len == 0 || len > sizeof(res.len))
300 {
301 return ASN1_INVALID;
302 }
303 res.len = 0;
304 while (len-- > 0)
305 {
306 res.len = 256 * res.len + blob->ptr[0];
307 *blob = chunk_skip(*blob, 1);
308 }
309 }
310 if (res.len > blob->len)
311 {
312 return ASN1_INVALID;
313 }
314 res.ptr = blob->ptr;
315 *blob = chunk_skip(*blob, res.len);
316 /* updating inner not before we are finished allows a caller to pass
317 * blob = inner */
318 *inner = res;
319 return type;
320 }
321
322 static const int days[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
323 static const int tm_leap_1970 = 477;
324
325 /**
326 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
327 */
328 time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
329 {
330 int tm_year, tm_mon, tm_day, tm_hour, tm_min, tm_sec;
331 int tm_leap_4, tm_leap_100, tm_leap_400, tm_leap;
332 int tz_hour, tz_min, tz_offset;
333 time_t tm_days, tm_secs;
334 u_char *eot = NULL;
335
336 if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
337 {
338 tz_offset = 0; /* Zulu time with a zero time zone offset */
339 }
340 else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
341 {
342 if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
343 {
344 return 0; /* error in positive timezone offset format */
345 }
346 tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */
347 }
348 else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
349 {
350 if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
351 {
352 return 0; /* error in negative timezone offset format */
353 }
354 tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */
355 }
356 else
357 {
358 return 0; /* error in time format */
359 }
360
361 /* parse ASN.1 time string */
362 {
363 const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
364 "%4d%2d%2d%2d%2d";
365
366 if (sscanf(utctime->ptr, format, &tm_year, &tm_mon, &tm_day,
367 &tm_hour, &tm_min) != 5)
368 {
369 return 0; /* error in [yy]yymmddhhmm time format */
370 }
371 }
372
373 /* is there a seconds field? */
374 if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
375 {
376 if (sscanf(eot-2, "%2d", &tm_sec) != 1)
377 {
378 return 0; /* error in ss seconds field format */
379 }
380 }
381 else
382 {
383 tm_sec = 0;
384 }
385
386 /* representation of two-digit years */
387 if (type == ASN1_UTCTIME)
388 {
389 tm_year += (tm_year < 50) ? 2000 : 1900;
390 }
391
392 /* prevent large 32 bit integer overflows */
393 if (sizeof(time_t) == 4 && tm_year > 2038)
394 {
395 return TIME_32_BIT_SIGNED_MAX;
396 }
397
398 /* representation of months as 0..11*/
399 if (tm_mon < 1 || tm_mon > 12)
400 {
401 return 0; /* error in month format */
402 }
403 tm_mon--;
404
405 /* representation of days as 0..30 */
406 tm_day--;
407
408 /* number of leap years between last year and 1970? */
409 tm_leap_4 = (tm_year - 1) / 4;
410 tm_leap_100 = tm_leap_4 / 25;
411 tm_leap_400 = tm_leap_100 / 4;
412 tm_leap = tm_leap_4 - tm_leap_100 + tm_leap_400 - tm_leap_1970;
413
414 /* if date later then February, is the current year a leap year? */
415 if (tm_mon > 1 && (tm_year % 4 == 0) &&
416 (tm_year % 100 != 0 || tm_year % 400 == 0))
417 {
418 tm_leap++;
419 }
420 tm_days = 365 * (tm_year - 1970) + days[tm_mon] + tm_day + tm_leap;
421 tm_secs = 60 * (60 * (24 * tm_days + tm_hour) + tm_min) + tm_sec - tz_offset;
422
423 /* has a 32 bit signed integer overflow occurred? */
424 return (tm_secs < 0) ? TIME_32_BIT_SIGNED_MAX : tm_secs;
425 }
426
427 /**
428 * Convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
429 */
430 chunk_t asn1_from_time(const time_t *time, asn1_t type)
431 {
432 int offset;
433 const char *format;
434 char buf[BUF_LEN];
435 chunk_t formatted_time;
436 struct tm t;
437
438 gmtime_r(time, &t);
439 /* RFC 5280 says that dates through the year 2049 MUST be encoded as UTCTIME
440 * and dates in 2050 or later MUST be encoded as GENERALIZEDTIME. We only
441 * enforce the latter to avoid overflows but allow callers to force the
442 * encoding to GENERALIZEDTIME */
443 type = (t.tm_year >= 150) ? ASN1_GENERALIZEDTIME : type;
444 if (type == ASN1_GENERALIZEDTIME)
445 {
446 format = "%04d%02d%02d%02d%02d%02dZ";
447 offset = 1900;
448 }
449 else /* ASN1_UTCTIME */
450 {
451 format = "%02d%02d%02d%02d%02d%02dZ";
452 offset = (t.tm_year < 100) ? 0 : -100;
453 }
454 snprintf(buf, BUF_LEN, format, t.tm_year + offset,
455 t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
456 formatted_time.ptr = buf;
457 formatted_time.len = strlen(buf);
458 return asn1_simple_object(type, formatted_time);
459 }
460
461 /*
462 * Defined in header.
463 */
464 void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private)
465 {
466 int oid;
467
468 switch (type)
469 {
470 case ASN1_OID:
471 oid = asn1_known_oid(object);
472 if (oid == OID_UNKNOWN)
473 {
474 char *oid_str = asn1_oid_to_string(object);
475
476 if (!oid_str)
477 {
478 break;
479 }
480 DBG2(DBG_ASN, " %s", oid_str);
481 free(oid_str);
482 }
483 else
484 {
485 DBG2(DBG_ASN, " '%s'", oid_names[oid].name);
486 }
487 return;
488 case ASN1_UTF8STRING:
489 case ASN1_IA5STRING:
490 case ASN1_PRINTABLESTRING:
491 case ASN1_T61STRING:
492 case ASN1_VISIBLESTRING:
493 DBG2(DBG_ASN, " '%.*s'", (int)object.len, object.ptr);
494 return;
495 case ASN1_UTCTIME:
496 case ASN1_GENERALIZEDTIME:
497 {
498 time_t time = asn1_to_time(&object, type);
499
500 DBG2(DBG_ASN, " '%T'", &time, TRUE);
501 }
502 return;
503 default:
504 break;
505 }
506 if (private)
507 {
508 DBG4(DBG_ASN, "%B", &object);
509 }
510 else
511 {
512 DBG3(DBG_ASN, "%B", &object);
513 }
514 }
515
516 /**
517 * parse an ASN.1 simple type
518 */
519 bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name)
520 {
521 size_t len;
522
523 /* an ASN.1 object must possess at least a tag and length field */
524 if (object->len < 2)
525 {
526 DBG2(DBG_ASN, "L%d - %s: ASN.1 object smaller than 2 octets", level,
527 name);
528 return FALSE;
529 }
530
531 if (*object->ptr != type)
532 {
533 DBG2(DBG_ASN, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
534 level, name, type, *object->ptr);
535 return FALSE;
536 }
537
538 len = asn1_length(object);
539
540 if (len == ASN1_INVALID_LENGTH || object->len < len)
541 {
542 DBG2(DBG_ASN, "L%d - %s: length of ASN.1 object invalid or too large",
543 level, name);
544 return FALSE;
545 }
546
547 DBG2(DBG_ASN, "L%d - %s:", level, name);
548 asn1_debug_simple_object(*object, type, FALSE);
549 return TRUE;
550 }
551
552 /*
553 * Described in header
554 */
555 u_int64_t asn1_parse_integer_uint64(chunk_t blob)
556 {
557 u_int64_t val = 0;
558 int i;
559
560 for (i = 0; i < blob.len; i++)
561 { /* if it is longer than 8 bytes, we just use the 8 LSBs */
562 val <<= 8;
563 val |= (u_int64_t)blob.ptr[i];
564 }
565 return val;
566 }
567
568 /**
569 * ASN.1 definition of an algorithmIdentifier
570 */
571 static const asn1Object_t algorithmIdentifierObjects[] = {
572 { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
573 { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
574 { 1, "parameters", ASN1_OID, ASN1_RAW|ASN1_OPT }, /* 2 */
575 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
576 { 1, "parameters", ASN1_SEQUENCE, ASN1_RAW|ASN1_OPT }, /* 4 */
577 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 5 */
578 { 1, "parameters", ASN1_OCTET_STRING, ASN1_RAW|ASN1_OPT }, /* 6 */
579 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */
580 { 0, "exit", ASN1_EOC, ASN1_EXIT }
581 };
582 #define ALGORITHM_ID_ALG 1
583 #define ALGORITHM_ID_PARAMETERS_OID 2
584 #define ALGORITHM_ID_PARAMETERS_SEQ 4
585 #define ALGORITHM_ID_PARAMETERS_OCT 6
586
587 /*
588 * Defined in header
589 */
590 int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
591 {
592 asn1_parser_t *parser;
593 chunk_t object;
594 int objectID;
595 int alg = OID_UNKNOWN;
596
597 parser = asn1_parser_create(algorithmIdentifierObjects, blob);
598 parser->set_top_level(parser, level0);
599
600 while (parser->iterate(parser, &objectID, &object))
601 {
602 switch (objectID)
603 {
604 case ALGORITHM_ID_ALG:
605 alg = asn1_known_oid(object);
606 break;
607 case ALGORITHM_ID_PARAMETERS_OID:
608 case ALGORITHM_ID_PARAMETERS_SEQ:
609 case ALGORITHM_ID_PARAMETERS_OCT:
610 if (parameters != NULL)
611 {
612 *parameters = object;
613 }
614 break;
615 default:
616 break;
617 }
618 }
619 parser->destroy(parser);
620 return alg;
621 }
622
623 /*
624 * tests if a blob contains a valid ASN.1 set or sequence
625 */
626 bool is_asn1(chunk_t blob)
627 {
628 u_int len;
629 u_char tag;
630
631 if (!blob.len || !blob.ptr)
632 {
633 return FALSE;
634 }
635
636 tag = *blob.ptr;
637 if (tag != ASN1_SEQUENCE && tag != ASN1_SET && tag != ASN1_OCTET_STRING)
638 {
639 DBG2(DBG_ASN, " file content is not binary ASN.1");
640 return FALSE;
641 }
642
643 len = asn1_length(&blob);
644
645 /* exact match */
646 if (len == blob.len)
647 {
648 return TRUE;
649 }
650
651 /* some websites append a surplus newline character to the blob */
652 if (len + 1 == blob.len && *(blob.ptr + len) == '\n')
653 {
654 return TRUE;
655 }
656
657 DBG2(DBG_ASN, " file size does not match ASN.1 coded length");
658 return FALSE;
659 }
660
661 /*
662 * Defined in header.
663 */
664 bool asn1_is_printablestring(chunk_t str)
665 {
666 const char printablestring_charset[] =
667 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
668 u_int i;
669
670 for (i = 0; i < str.len; i++)
671 {
672 if (strchr(printablestring_charset, str.ptr[i]) == NULL)
673 return FALSE;
674 }
675 return TRUE;
676 }
677
678 /**
679 * codes ASN.1 lengths up to a size of 16'777'215 bytes
680 */
681 static void asn1_code_length(size_t length, chunk_t *code)
682 {
683 if (length < 128)
684 {
685 code->ptr[0] = length;
686 code->len = 1;
687 }
688 else if (length < 256)
689 {
690 code->ptr[0] = 0x81;
691 code->ptr[1] = (u_char) length;
692 code->len = 2;
693 }
694 else if (length < 65536)
695 {
696 code->ptr[0] = 0x82;
697 code->ptr[1] = length >> 8;
698 code->ptr[2] = length & 0x00ff;
699 code->len = 3;
700 }
701 else
702 {
703 code->ptr[0] = 0x83;
704 code->ptr[1] = length >> 16;
705 code->ptr[2] = (length >> 8) & 0x00ff;
706 code->ptr[3] = length & 0x0000ff;
707 code->len = 4;
708 }
709 }
710
711 /**
712 * build an empty asn.1 object with tag and length fields already filled in
713 */
714 u_char* asn1_build_object(chunk_t *object, asn1_t type, size_t datalen)
715 {
716 u_char length_buf[4];
717 chunk_t length = { length_buf, 0 };
718 u_char *pos;
719
720 /* code the asn.1 length field */
721 asn1_code_length(datalen, &length);
722
723 /* allocate memory for the asn.1 TLV object */
724 object->len = 1 + length.len + datalen;
725 object->ptr = malloc(object->len);
726
727 /* set position pointer at the start of the object */
728 pos = object->ptr;
729
730 /* copy the asn.1 tag field and advance the pointer */
731 *pos++ = type;
732
733 /* copy the asn.1 length field and advance the pointer */
734 memcpy(pos, length.ptr, length.len);
735 pos += length.len;
736
737 return pos;
738 }
739
740 /**
741 * Build a simple ASN.1 object
742 */
743 chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
744 {
745 chunk_t object;
746
747 u_char *pos = asn1_build_object(&object, tag, content.len);
748 memcpy(pos, content.ptr, content.len);
749 pos += content.len;
750
751 return object;
752 }
753
754 /**
755 * Build an ASN.1 BIT_STRING object
756 */
757 chunk_t asn1_bitstring(const char *mode, chunk_t content)
758 {
759 chunk_t object;
760 u_char *pos = asn1_build_object(&object, ASN1_BIT_STRING, 1 + content.len);
761
762 *pos++ = 0x00;
763 memcpy(pos, content.ptr, content.len);
764 if (*mode == 'm')
765 {
766 free(content.ptr);
767 }
768 return object;
769 }
770
771 /**
772 * Build an ASN.1 INTEGER object
773 */
774 chunk_t asn1_integer(const char *mode, chunk_t content)
775 {
776 chunk_t object;
777 size_t len;
778 u_char *pos;
779
780 if (content.len == 0)
781 { /* make sure 0 is encoded properly */
782 content = chunk_from_chars(0x00);
783 }
784
785 /* ASN.1 integers must be positive numbers in two's complement */
786 len = content.len + ((*content.ptr & 0x80) ? 1 : 0);
787 pos = asn1_build_object(&object, ASN1_INTEGER, len);
788 if (len > content.len)
789 {
790 *pos++ = 0x00;
791 }
792 if (len)
793 {
794 memcpy(pos, content.ptr, content.len);
795 }
796 if (*mode == 'm')
797 {
798 free(content.ptr);
799 }
800 return object;
801 }
802
803 /**
804 * Build an ASN.1 object from a variable number of individual chunks.
805 * Depending on the mode, chunks either are moved ('m') or copied ('c').
806 */
807 chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
808 {
809 chunk_t construct;
810 va_list chunks;
811 u_char *pos;
812 int i;
813 int count = strlen(mode);
814
815 /* sum up lengths of individual chunks */
816 va_start(chunks, mode);
817 construct.len = 0;
818 for (i = 0; i < count; i++)
819 {
820 chunk_t ch = va_arg(chunks, chunk_t);
821 construct.len += ch.len;
822 }
823 va_end(chunks);
824
825 /* allocate needed memory for construct */
826 pos = asn1_build_object(&construct, type, construct.len);
827
828 /* copy or move the chunks */
829 va_start(chunks, mode);
830 for (i = 0; i < count; i++)
831 {
832 chunk_t ch = va_arg(chunks, chunk_t);
833
834 memcpy(pos, ch.ptr, ch.len);
835 pos += ch.len;
836
837 switch (*mode++)
838 {
839 case 's':
840 chunk_clear(&ch);
841 break;
842 case 'm':
843 free(ch.ptr);
844 break;
845 default:
846 break;
847 }
848 }
849 va_end(chunks);
850
851 return construct;
852 }
853
854 /**
855 * ASN.1 definition of time
856 */
857 static const asn1Object_t timeObjects[] = {
858 { 0, "utcTime", ASN1_UTCTIME, ASN1_OPT|ASN1_BODY }, /* 0 */
859 { 0, "end opt", ASN1_EOC, ASN1_END }, /* 1 */
860 { 0, "generalizeTime", ASN1_GENERALIZEDTIME, ASN1_OPT|ASN1_BODY }, /* 2 */
861 { 0, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
862 { 0, "exit", ASN1_EOC, ASN1_EXIT }
863 };
864 #define TIME_UTC 0
865 #define TIME_GENERALIZED 2
866
867 /**
868 * extracts and converts a UTCTIME or GENERALIZEDTIME object
869 */
870 time_t asn1_parse_time(chunk_t blob, int level0)
871 {
872 asn1_parser_t *parser;
873 chunk_t object;
874 int objectID;
875 time_t utc_time = 0;
876
877 parser= asn1_parser_create(timeObjects, blob);
878 parser->set_top_level(parser, level0);
879
880 while (parser->iterate(parser, &objectID, &object))
881 {
882 if (objectID == TIME_UTC || objectID == TIME_GENERALIZED)
883 {
884 utc_time = asn1_to_time(&object, (objectID == TIME_UTC)
885 ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME);
886 }
887 }
888 parser->destroy(parser);
889 return utc_time;
890 }