fixed asn1_oid_to_string() conversion
[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 <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, 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[32];
133 char *end;
134 int i = 0, pos = 0;
135 u_int 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 if (val < 128)
156 {
157 buf[pos++] = val;
158 }
159 else
160 {
161 buf[pos++] = 128 | (val >> 7);
162 buf[pos++] = (val % 256) & 0x7F;
163 }
164 break;
165 }
166 }
167 enumerator->destroy(enumerator);
168
169 return chunk_clone(chunk_create(buf, pos));
170 }
171
172 /*
173 * Defined in header.
174 */
175 char *asn1_oid_to_string(chunk_t oid)
176 {
177 char buf[64], *pos = buf;
178 int len;
179 u_int val;
180
181 if (!oid.len)
182 {
183 return NULL;
184 }
185 val = oid.ptr[0] / 40;
186 len = snprintf(buf, sizeof(buf), "%u.%u", val, oid.ptr[0] - val * 40);
187 oid = chunk_skip(oid, 1);
188 if (len < 0 || len >= sizeof(buf))
189 {
190 return NULL;
191 }
192 pos += len;
193 val = 0;
194
195 while (oid.len)
196 {
197 val = (val << 7) + (u_int)(oid.ptr[0] & 0x7f);
198
199 if (oid.ptr[0] < 128)
200 {
201 len = snprintf(pos, sizeof(buf) + buf - pos, ".%u", val);
202 if (len < 0 || len >= sizeof(buf) + buf - pos)
203 {
204 return NULL;
205 }
206 pos += len;
207 val = 0;
208 }
209 oid = chunk_skip(oid, 1);
210 }
211 return strdup(buf);
212 }
213
214 /*
215 * Defined in header.
216 */
217 size_t asn1_length(chunk_t *blob)
218 {
219 u_char n;
220 size_t len;
221
222 if (blob->len < 2)
223 {
224 DBG2(DBG_LIB, "insufficient number of octets to parse ASN.1 length");
225 return ASN1_INVALID_LENGTH;
226 }
227
228 /* read length field, skip tag and length */
229 n = blob->ptr[1];
230 *blob = chunk_skip(*blob, 2);
231
232 if ((n & 0x80) == 0)
233 { /* single length octet */
234 if (n > blob->len)
235 {
236 DBG2(DBG_LIB, "length is larger than remaining blob size");
237 return ASN1_INVALID_LENGTH;
238 }
239 return n;
240 }
241
242 /* composite length, determine number of length octets */
243 n &= 0x7f;
244
245 if (n == 0 || n > blob->len)
246 {
247 DBG2(DBG_LIB, "number of length octets invalid");
248 return ASN1_INVALID_LENGTH;
249 }
250
251 if (n > sizeof(len))
252 {
253 DBG2(DBG_LIB, "number of length octets is larger than limit of"
254 " %d octets", (int)sizeof(len));
255 return ASN1_INVALID_LENGTH;
256 }
257
258 len = 0;
259
260 while (n-- > 0)
261 {
262 len = 256*len + *blob->ptr++;
263 blob->len--;
264 }
265 if (len > blob->len)
266 {
267 DBG2(DBG_LIB, "length is larger than remaining blob size");
268 return ASN1_INVALID_LENGTH;
269 }
270 return len;
271 }
272
273 /*
274 * See header.
275 */
276 int asn1_unwrap(chunk_t *blob, chunk_t *inner)
277 {
278 chunk_t res;
279 u_char len;
280 int type;
281
282 if (blob->len < 2)
283 {
284 return ASN1_INVALID;
285 }
286 type = blob->ptr[0];
287 len = blob->ptr[1];
288 *blob = chunk_skip(*blob, 2);
289
290 if ((len & 0x80) == 0)
291 { /* single length octet */
292 res.len = len;
293 }
294 else
295 { /* composite length, determine number of length octets */
296 len &= 0x7f;
297 if (len == 0 || len > sizeof(res.len))
298 {
299 return ASN1_INVALID;
300 }
301 res.len = 0;
302 while (len-- > 0)
303 {
304 res.len = 256 * res.len + blob->ptr[0];
305 *blob = chunk_skip(*blob, 1);
306 }
307 }
308 if (res.len > blob->len)
309 {
310 return ASN1_INVALID;
311 }
312 res.ptr = blob->ptr;
313 *blob = chunk_skip(*blob, res.len);
314 /* updating inner not before we are finished allows a caller to pass
315 * blob = inner */
316 *inner = res;
317 return type;
318 }
319
320 static const int days[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
321 static const int tm_leap_1970 = 477;
322
323 /**
324 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
325 */
326 time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
327 {
328 int tm_year, tm_mon, tm_day, tm_days, tm_hour, tm_min, tm_sec;
329 int tm_leap_4, tm_leap_100, tm_leap_400, tm_leap;
330 int tz_hour, tz_min, tz_offset;
331 time_t tm_secs;
332 u_char *eot = NULL;
333
334 if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
335 {
336 tz_offset = 0; /* Zulu time with a zero time zone offset */
337 }
338 else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
339 {
340 if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
341 {
342 return 0; /* error in positive timezone offset format */
343 }
344 tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */
345 }
346 else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
347 {
348 if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
349 {
350 return 0; /* error in negative timezone offset format */
351 }
352 tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */
353 }
354 else
355 {
356 return 0; /* error in time format */
357 }
358
359 /* parse ASN.1 time string */
360 {
361 const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
362 "%4d%2d%2d%2d%2d";
363
364 if (sscanf(utctime->ptr, format, &tm_year, &tm_mon, &tm_day,
365 &tm_hour, &tm_min) != 5)
366 {
367 return 0; /* error in [yy]yymmddhhmm time format */
368 }
369 }
370
371 /* is there a seconds field? */
372 if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
373 {
374 if (sscanf(eot-2, "%2d", &tm_sec) != 1)
375 {
376 return 0; /* error in ss seconds field format */
377 }
378 }
379 else
380 {
381 tm_sec = 0;
382 }
383
384 /* representation of two-digit years */
385 if (type == ASN1_UTCTIME)
386 {
387 tm_year += (tm_year < 50) ? 2000 : 1900;
388 }
389
390 /* prevent large 32 bit integer overflows */
391 if (sizeof(time_t) == 4 && tm_year > 2038)
392 {
393 return TIME_32_BIT_SIGNED_MAX;
394 }
395
396 /* representation of months as 0..11*/
397 if (tm_mon < 1 || tm_mon > 12)
398 {
399 return 0; /* error in month format */
400 }
401 tm_mon--;
402
403 /* representation of days as 0..30 */
404 tm_day--;
405
406 /* number of leap years between last year and 1970? */
407 tm_leap_4 = (tm_year - 1) / 4;
408 tm_leap_100 = tm_leap_4 / 25;
409 tm_leap_400 = tm_leap_100 / 4;
410 tm_leap = tm_leap_4 - tm_leap_100 + tm_leap_400 - tm_leap_1970;
411
412 /* if date later then February, is the current year a leap year? */
413 if (tm_mon > 1 && (tm_year % 4 == 0) &&
414 (tm_year % 100 != 0 || tm_year % 400 == 0))
415 {
416 tm_leap++;
417 }
418 tm_days = 365 * (tm_year - 1970) + days[tm_mon] + tm_day + tm_leap;
419 tm_secs = 60 * (60 * (24 * tm_days + tm_hour) + tm_min) + tm_sec - tz_offset;
420
421 /* has a 32 bit signed integer overflow occurred? */
422 return (tm_secs < 0) ? TIME_32_BIT_SIGNED_MAX : tm_secs;
423 }
424
425 /**
426 * Convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
427 */
428 chunk_t asn1_from_time(const time_t *time, asn1_t type)
429 {
430 int offset;
431 const char *format;
432 char buf[BUF_LEN];
433 chunk_t formatted_time;
434 struct tm t;
435
436 gmtime_r(time, &t);
437 if (type == ASN1_GENERALIZEDTIME)
438 {
439 format = "%04d%02d%02d%02d%02d%02dZ";
440 offset = 1900;
441 }
442 else /* ASN1_UTCTIME */
443 {
444 format = "%02d%02d%02d%02d%02d%02dZ";
445 offset = (t.tm_year < 100)? 0 : -100;
446 }
447 snprintf(buf, BUF_LEN, format, t.tm_year + offset,
448 t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
449 formatted_time.ptr = buf;
450 formatted_time.len = strlen(buf);
451 return asn1_simple_object(type, formatted_time);
452 }
453
454 /*
455 * Defined in header.
456 */
457 void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private)
458 {
459 int oid;
460
461 switch (type)
462 {
463 case ASN1_OID:
464 oid = asn1_known_oid(object);
465 if (oid != OID_UNKNOWN)
466 {
467 DBG2(DBG_LIB, " '%s'", oid_names[oid].name);
468 return;
469 }
470 break;
471 case ASN1_UTF8STRING:
472 case ASN1_IA5STRING:
473 case ASN1_PRINTABLESTRING:
474 case ASN1_T61STRING:
475 case ASN1_VISIBLESTRING:
476 DBG2(DBG_LIB, " '%.*s'", (int)object.len, object.ptr);
477 return;
478 case ASN1_UTCTIME:
479 case ASN1_GENERALIZEDTIME:
480 {
481 time_t time = asn1_to_time(&object, type);
482
483 DBG2(DBG_LIB, " '%T'", &time, TRUE);
484 }
485 return;
486 default:
487 break;
488 }
489 if (private)
490 {
491 DBG4(DBG_LIB, "%B", &object);
492 }
493 else
494 {
495 DBG3(DBG_LIB, "%B", &object);
496 }
497 }
498
499 /**
500 * parse an ASN.1 simple type
501 */
502 bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name)
503 {
504 size_t len;
505
506 /* an ASN.1 object must possess at least a tag and length field */
507 if (object->len < 2)
508 {
509 DBG2(DBG_LIB, "L%d - %s: ASN.1 object smaller than 2 octets", level,
510 name);
511 return FALSE;
512 }
513
514 if (*object->ptr != type)
515 {
516 DBG2(DBG_LIB, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
517 level, name, type, *object->ptr);
518 return FALSE;
519 }
520
521 len = asn1_length(object);
522
523 if (len == ASN1_INVALID_LENGTH || object->len < len)
524 {
525 DBG2(DBG_LIB, "L%d - %s: length of ASN.1 object invalid or too large",
526 level, name);
527 return FALSE;
528 }
529
530 DBG2(DBG_LIB, "L%d - %s:", level, name);
531 asn1_debug_simple_object(*object, type, FALSE);
532 return TRUE;
533 }
534
535 /**
536 * ASN.1 definition of an algorithmIdentifier
537 */
538 static const asn1Object_t algorithmIdentifierObjects[] = {
539 { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
540 { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
541 { 1, "parameters", ASN1_EOC, ASN1_RAW|ASN1_OPT }, /* 2 */
542 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
543 { 0, "exit", ASN1_EOC, ASN1_EXIT }
544 };
545 #define ALGORITHM_ID_ALG 1
546 #define ALGORITHM_ID_PARAMETERS 2
547
548 /*
549 * Defined in header
550 */
551 int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
552 {
553 asn1_parser_t *parser;
554 chunk_t object;
555 int objectID;
556 int alg = OID_UNKNOWN;
557
558 parser = asn1_parser_create(algorithmIdentifierObjects, blob);
559 parser->set_top_level(parser, level0);
560
561 while (parser->iterate(parser, &objectID, &object))
562 {
563 switch (objectID)
564 {
565 case ALGORITHM_ID_ALG:
566 alg = asn1_known_oid(object);
567 break;
568 case ALGORITHM_ID_PARAMETERS:
569 if (parameters != NULL)
570 {
571 *parameters = object;
572 }
573 break;
574 default:
575 break;
576 }
577 }
578 parser->destroy(parser);
579 return alg;
580 }
581
582 /*
583 * tests if a blob contains a valid ASN.1 set or sequence
584 */
585 bool is_asn1(chunk_t blob)
586 {
587 u_int len;
588 u_char tag;
589
590 if (!blob.len || !blob.ptr)
591 {
592 return FALSE;
593 }
594
595 tag = *blob.ptr;
596 if (tag != ASN1_SEQUENCE && tag != ASN1_SET && tag != ASN1_OCTET_STRING)
597 {
598 DBG2(DBG_LIB, " file content is not binary ASN.1");
599 return FALSE;
600 }
601
602 len = asn1_length(&blob);
603
604 /* exact match */
605 if (len == blob.len)
606 {
607 return TRUE;
608 }
609
610 /* some websites append a surplus newline character to the blob */
611 if (len + 1 == blob.len && *(blob.ptr + len) == '\n')
612 {
613 return TRUE;
614 }
615
616 DBG2(DBG_LIB, " file size does not match ASN.1 coded length");
617 return FALSE;
618 }
619
620 /*
621 * Defined in header.
622 */
623 bool asn1_is_printablestring(chunk_t str)
624 {
625 const char printablestring_charset[] =
626 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
627 u_int i;
628
629 for (i = 0; i < str.len; i++)
630 {
631 if (strchr(printablestring_charset, str.ptr[i]) == NULL)
632 return FALSE;
633 }
634 return TRUE;
635 }
636
637 /**
638 * codes ASN.1 lengths up to a size of 16'777'215 bytes
639 */
640 static void asn1_code_length(size_t length, chunk_t *code)
641 {
642 if (length < 128)
643 {
644 code->ptr[0] = length;
645 code->len = 1;
646 }
647 else if (length < 256)
648 {
649 code->ptr[0] = 0x81;
650 code->ptr[1] = (u_char) length;
651 code->len = 2;
652 }
653 else if (length < 65536)
654 {
655 code->ptr[0] = 0x82;
656 code->ptr[1] = length >> 8;
657 code->ptr[2] = length & 0x00ff;
658 code->len = 3;
659 }
660 else
661 {
662 code->ptr[0] = 0x83;
663 code->ptr[1] = length >> 16;
664 code->ptr[2] = (length >> 8) & 0x00ff;
665 code->ptr[3] = length & 0x0000ff;
666 code->len = 4;
667 }
668 }
669
670 /**
671 * build an empty asn.1 object with tag and length fields already filled in
672 */
673 u_char* asn1_build_object(chunk_t *object, asn1_t type, size_t datalen)
674 {
675 u_char length_buf[4];
676 chunk_t length = { length_buf, 0 };
677 u_char *pos;
678
679 /* code the asn.1 length field */
680 asn1_code_length(datalen, &length);
681
682 /* allocate memory for the asn.1 TLV object */
683 object->len = 1 + length.len + datalen;
684 object->ptr = malloc(object->len);
685
686 /* set position pointer at the start of the object */
687 pos = object->ptr;
688
689 /* copy the asn.1 tag field and advance the pointer */
690 *pos++ = type;
691
692 /* copy the asn.1 length field and advance the pointer */
693 memcpy(pos, length.ptr, length.len);
694 pos += length.len;
695
696 return pos;
697 }
698
699 /**
700 * Build a simple ASN.1 object
701 */
702 chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
703 {
704 chunk_t object;
705
706 u_char *pos = asn1_build_object(&object, tag, content.len);
707 memcpy(pos, content.ptr, content.len);
708 pos += content.len;
709
710 return object;
711 }
712
713 /**
714 * Build an ASN.1 BIT_STRING object
715 */
716 chunk_t asn1_bitstring(const char *mode, chunk_t content)
717 {
718 chunk_t object;
719 u_char *pos = asn1_build_object(&object, ASN1_BIT_STRING, 1 + content.len);
720
721 *pos++ = 0x00;
722 memcpy(pos, content.ptr, content.len);
723 if (*mode == 'm')
724 {
725 free(content.ptr);
726 }
727 return object;
728 }
729
730 /**
731 * Build an ASN.1 INTEGER object
732 */
733 chunk_t asn1_integer(const char *mode, chunk_t content)
734 {
735 chunk_t object;
736 size_t len;
737 u_char *pos;
738
739 if (content.len == 0 || (content.len == 1 && *content.ptr == 0x00))
740 {
741 /* a zero ASN.1 integer does not have a value field */
742 len = 0;
743 }
744 else
745 {
746 /* ASN.1 integers must be positive numbers in two's complement */
747 len = content.len + ((*content.ptr & 0x80) ? 1 : 0);
748 }
749 pos = asn1_build_object(&object, ASN1_INTEGER, len);
750 if (len > content.len)
751 {
752 *pos++ = 0x00;
753 }
754 if (len)
755 {
756 memcpy(pos, content.ptr, content.len);
757 }
758 if (*mode == 'm')
759 {
760 free(content.ptr);
761 }
762 return object;
763 }
764
765 /**
766 * Build an ASN.1 object from a variable number of individual chunks.
767 * Depending on the mode, chunks either are moved ('m') or copied ('c').
768 */
769 chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
770 {
771 chunk_t construct;
772 va_list chunks;
773 u_char *pos;
774 int i;
775 int count = strlen(mode);
776
777 /* sum up lengths of individual chunks */
778 va_start(chunks, mode);
779 construct.len = 0;
780 for (i = 0; i < count; i++)
781 {
782 chunk_t ch = va_arg(chunks, chunk_t);
783 construct.len += ch.len;
784 }
785 va_end(chunks);
786
787 /* allocate needed memory for construct */
788 pos = asn1_build_object(&construct, type, construct.len);
789
790 /* copy or move the chunks */
791 va_start(chunks, mode);
792 for (i = 0; i < count; i++)
793 {
794 chunk_t ch = va_arg(chunks, chunk_t);
795
796 memcpy(pos, ch.ptr, ch.len);
797 pos += ch.len;
798
799 switch (*mode++)
800 {
801 case 's':
802 chunk_clear(&ch);
803 break;
804 case 'm':
805 free(ch.ptr);
806 break;
807 default:
808 break;
809 }
810 }
811 va_end(chunks);
812
813 return construct;
814 }
815
816 /**
817 * ASN.1 definition of time
818 */
819 static const asn1Object_t timeObjects[] = {
820 { 0, "utcTime", ASN1_UTCTIME, ASN1_OPT|ASN1_BODY }, /* 0 */
821 { 0, "end opt", ASN1_EOC, ASN1_END }, /* 1 */
822 { 0, "generalizeTime", ASN1_GENERALIZEDTIME, ASN1_OPT|ASN1_BODY }, /* 2 */
823 { 0, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
824 { 0, "exit", ASN1_EOC, ASN1_EXIT }
825 };
826 #define TIME_UTC 0
827 #define TIME_GENERALIZED 2
828
829 /**
830 * extracts and converts a UTCTIME or GENERALIZEDTIME object
831 */
832 time_t asn1_parse_time(chunk_t blob, int level0)
833 {
834 asn1_parser_t *parser;
835 chunk_t object;
836 int objectID;
837 time_t utc_time = 0;
838
839 parser= asn1_parser_create(timeObjects, blob);
840 parser->set_top_level(parser, level0);
841
842 while (parser->iterate(parser, &objectID, &object))
843 {
844 if (objectID == TIME_UTC || objectID == TIME_GENERALIZED)
845 {
846 utc_time = asn1_to_time(&object, (objectID == TIME_UTC)
847 ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME);
848 }
849 }
850 parser->destroy(parser);
851 return utc_time;
852 }