fixed timezone compensation when parsing ASN.1 dates
[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 #include <pthread.h>
22
23 #include <utils.h>
24 #include <debug.h>
25
26 #include "oid.h"
27 #include "asn1.h"
28 #include "asn1_parser.h"
29
30 /**
31 * some common prefabricated ASN.1 constants
32 */
33 static u_char ASN1_INTEGER_0_str[] = { 0x02, 0x00 };
34 static u_char ASN1_INTEGER_1_str[] = { 0x02, 0x01, 0x01 };
35 static u_char ASN1_INTEGER_2_str[] = { 0x02, 0x01, 0x02 };
36
37 const chunk_t ASN1_INTEGER_0 = chunk_from_buf(ASN1_INTEGER_0_str);
38 const chunk_t ASN1_INTEGER_1 = chunk_from_buf(ASN1_INTEGER_1_str);
39 const chunk_t ASN1_INTEGER_2 = chunk_from_buf(ASN1_INTEGER_2_str);
40
41 /**
42 * some popular algorithmIdentifiers
43 */
44
45 static u_char ASN1_md2_id_str[] = {
46 0x30, 0x0c,
47 0x06, 0x08,
48 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x02,
49 0x05,0x00,
50 };
51
52 static u_char ASN1_md5_id_str[] = {
53 0x30, 0x0C,
54 0x06, 0x08,
55 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05,
56 0x05, 0x00
57 };
58
59 static u_char ASN1_sha1_id_str[] = {
60 0x30, 0x09,
61 0x06, 0x05,
62 0x2B, 0x0E,0x03, 0x02, 0x1A,
63 0x05, 0x00
64 };
65
66 static u_char ASN1_sha256_id_str[] = {
67 0x30, 0x0d,
68 0x06, 0x09,
69 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
70 0x05, 0x00
71 };
72
73 static u_char ASN1_sha384_id_str[] = {
74 0x30, 0x0d,
75 0x06, 0x09,
76 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
77 0x05, 0x00
78 };
79
80 static u_char ASN1_sha512_id_str[] = {
81 0x30, 0x0d,
82 0x06, 0x09,
83 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
84 0x05,0x00
85 };
86
87 static u_char ASN1_md2WithRSA_id_str[] = {
88 0x30, 0x0D,
89 0x06, 0x09,
90 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02,
91 0x05, 0x00
92 };
93
94 static u_char ASN1_md5WithRSA_id_str[] = {
95 0x30, 0x0D,
96 0x06, 0x09,
97 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04,
98 0x05, 0x00
99 };
100
101 static u_char ASN1_sha1WithRSA_id_str[] = {
102 0x30, 0x0D,
103 0x06, 0x09,
104 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
105 0x05, 0x00
106 };
107
108 static u_char ASN1_sha256WithRSA_id_str[] = {
109 0x30, 0x0D,
110 0x06, 0x09,
111 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
112 0x05, 0x00
113 };
114
115 static u_char ASN1_sha384WithRSA_id_str[] = {
116 0x30, 0x0D,
117 0x06, 0x09,
118 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C,
119 0x05, 0x00
120 };
121
122 static u_char ASN1_sha512WithRSA_id_str[] = {
123 0x30, 0x0D,
124 0x06, 0x09,
125 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D,
126 0x05, 0x00
127 };
128
129 static u_char ASN1_rsaEncryption_id_str[] = {
130 0x30, 0x0D,
131 0x06, 0x09,
132 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
133 0x05, 0x00
134 };
135
136 static const chunk_t ASN1_md2_id = chunk_from_buf(ASN1_md2_id_str);
137 static const chunk_t ASN1_md5_id = chunk_from_buf(ASN1_md5_id_str);
138 static const chunk_t ASN1_sha1_id = chunk_from_buf(ASN1_sha1_id_str);
139 static const chunk_t ASN1_sha256_id = chunk_from_buf(ASN1_sha256_id_str);
140 static const chunk_t ASN1_sha384_id = chunk_from_buf(ASN1_sha384_id_str);
141 static const chunk_t ASN1_sha512_id = chunk_from_buf(ASN1_sha512_id_str);
142 static const chunk_t ASN1_rsaEncryption_id = chunk_from_buf(ASN1_rsaEncryption_id_str);
143 static const chunk_t ASN1_md2WithRSA_id = chunk_from_buf(ASN1_md2WithRSA_id_str);
144 static const chunk_t ASN1_md5WithRSA_id = chunk_from_buf(ASN1_md5WithRSA_id_str);
145 static const chunk_t ASN1_sha1WithRSA_id = chunk_from_buf(ASN1_sha1WithRSA_id_str);
146 static const chunk_t ASN1_sha256WithRSA_id = chunk_from_buf(ASN1_sha256WithRSA_id_str);
147 static const chunk_t ASN1_sha384WithRSA_id = chunk_from_buf(ASN1_sha384WithRSA_id_str);
148 static const chunk_t ASN1_sha512WithRSA_id = chunk_from_buf(ASN1_sha512WithRSA_id_str);
149
150 /*
151 * Defined in header.
152 */
153 chunk_t asn1_algorithmIdentifier(int oid)
154 {
155 switch (oid)
156 {
157 case OID_RSA_ENCRYPTION:
158 return ASN1_rsaEncryption_id;
159 case OID_MD2_WITH_RSA:
160 return ASN1_md2WithRSA_id;
161 case OID_MD5_WITH_RSA:
162 return ASN1_md5WithRSA_id;
163 case OID_SHA1_WITH_RSA:
164 return ASN1_sha1WithRSA_id;
165 case OID_SHA256_WITH_RSA:
166 return ASN1_sha256WithRSA_id;
167 case OID_SHA384_WITH_RSA:
168 return ASN1_sha384WithRSA_id;
169 case OID_SHA512_WITH_RSA:
170 return ASN1_sha512WithRSA_id;
171 case OID_MD2:
172 return ASN1_md2_id;
173 case OID_MD5:
174 return ASN1_md5_id;
175 case OID_SHA1:
176 return ASN1_sha1_id;
177 case OID_SHA256:
178 return ASN1_sha256_id;
179 case OID_SHA384:
180 return ASN1_sha384_id;
181 case OID_SHA512:
182 return ASN1_sha512_id;
183 default:
184 return chunk_empty;
185 }
186 }
187
188 /*
189 * Defined in header.
190 */
191 int asn1_known_oid(chunk_t object)
192 {
193 int oid = 0;
194
195 while (object.len)
196 {
197 if (oid_names[oid].octet == *object.ptr)
198 {
199 if (--object.len == 0 || oid_names[oid].down == 0)
200 {
201 return oid; /* found terminal symbol */
202 }
203 else
204 {
205 object.ptr++; oid++; /* advance to next hex octet */
206 }
207 }
208 else
209 {
210 if (oid_names[oid].next)
211 {
212 oid = oid_names[oid].next;
213 }
214 else
215 {
216 return OID_UNKNOWN;
217 }
218 }
219 }
220 return -1;
221 }
222
223 /*
224 * Defined in header.
225 */
226 chunk_t asn1_build_known_oid(int n)
227 {
228 chunk_t oid;
229 int i;
230
231 if (n < 0 || n >= OID_MAX)
232 {
233 return chunk_empty;
234 }
235
236 i = oid_names[n].level + 1;
237 oid = chunk_alloc(2 + i);
238 oid.ptr[0] = ASN1_OID;
239 oid.ptr[1] = i;
240
241 do
242 {
243 if (oid_names[n].level >= i)
244 {
245 n--;
246 continue;
247 }
248 oid.ptr[--i + 2] = oid_names[n--].octet;
249 }
250 while (i > 0);
251
252 return oid;
253 }
254
255 /*
256 * Defined in header.
257 */
258 u_int asn1_length(chunk_t *blob)
259 {
260 u_char n;
261 size_t len;
262
263 /* advance from tag field on to length field */
264 blob->ptr++;
265 blob->len--;
266
267 /* read first octet of length field */
268 n = *blob->ptr++;
269 blob->len--;
270
271 if ((n & 0x80) == 0)
272 {/* single length octet */
273 return n;
274 }
275
276 /* composite length, determine number of length octets */
277 n &= 0x7f;
278
279 if (n > blob->len)
280 {
281 DBG2("number of length octets is larger than ASN.1 object");
282 return ASN1_INVALID_LENGTH;
283 }
284
285 if (n > sizeof(len))
286 {
287 DBG2("number of length octets is larger than limit of %d octets",
288 (int)sizeof(len));
289 return ASN1_INVALID_LENGTH;
290 }
291
292 len = 0;
293
294 while (n-- > 0)
295 {
296 len = 256*len + *blob->ptr++;
297 blob->len--;
298 }
299 return len;
300 }
301
302 #define TIME_MAX 0x7fffffff
303
304 #if !defined(HAVE_TIMEGM) && !defined(HAVE_VAR_TIMEZONE)
305 pthread_once_t utc_offset_once = PTHREAD_ONCE_INIT;
306 static time_t utc_offset;
307 static void init_utc_offset()
308 {
309 time_t now;
310 struct tm utc;
311 now = time(NULL);
312 gmtime_r(&now, &utc);
313 utc_offset = mktime(&utc) - now;
314 }
315 #endif
316
317 /**
318 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
319 */
320 time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
321 {
322 struct tm t;
323 time_t tc, tz_offset;
324 u_char *eot = NULL;
325
326 if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
327 {
328 tz_offset = 0; /* Zulu time with a zero time zone offset */
329 }
330 else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
331 {
332 int tz_hour, tz_min;
333
334 sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min);
335 tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */
336 }
337 else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
338 {
339 int tz_hour, tz_min;
340
341 sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min);
342 tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */
343 }
344 else
345 {
346 return 0; /* error in time format */
347 }
348
349 /* parse ASN.1 time string */
350 {
351 const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
352 "%4d%2d%2d%2d%2d";
353
354 sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday,
355 &t.tm_hour, &t.tm_min);
356 }
357
358 /* is there a seconds field? */
359 if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
360 {
361 sscanf(eot-2, "%2d", &t.tm_sec);
362 }
363 else
364 {
365 t.tm_sec = 0;
366 }
367
368 /* representation of year */
369 if (t.tm_year >= 1900)
370 {
371 t.tm_year -= 1900;
372 }
373 else if (t.tm_year >= 100)
374 {
375 return 0;
376 }
377 else if (t.tm_year < 50)
378 {
379 t.tm_year += 100;
380 }
381
382 /* representation of month 0..11*/
383 t.tm_mon--;
384
385 /* set daylight saving time to off */
386 t.tm_isdst = 0;
387
388 /* convert to time_t */
389 #ifdef HAVE_TIMEGM
390 tc = timegm(&t);
391 #else
392 tc = mktime(&t);
393 #endif
394 if (tc == -1)
395 {
396 return TIME_MAX;
397 }
398
399 /* if no conversion overflow occurred, compensate timezone */
400 #ifndef HAVE_TIMEGM
401 #ifdef HAVE_VAR_TIMEZONE
402 tz_offset += timezone;
403 #else
404 pthread_once(&utc_offset_once, init_utc_offset);
405 tz_offset += utc_offset;
406 #endif
407 #endif
408 return tc - tz_offset;
409 }
410
411 /**
412 * Convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
413 */
414 chunk_t asn1_from_time(const time_t *time, asn1_t type)
415 {
416 int offset;
417 const char *format;
418 char buf[BUF_LEN];
419 chunk_t formatted_time;
420 struct tm t;
421
422 gmtime_r(time, &t);
423 if (type == ASN1_GENERALIZEDTIME)
424 {
425 format = "%04d%02d%02d%02d%02d%02dZ";
426 offset = 1900;
427 }
428 else /* ASN1_UTCTIME */
429 {
430 format = "%02d%02d%02d%02d%02d%02dZ";
431 offset = (t.tm_year < 100)? 0 : -100;
432 }
433 snprintf(buf, BUF_LEN, format, t.tm_year + offset,
434 t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
435 formatted_time.ptr = buf;
436 formatted_time.len = strlen(buf);
437 return asn1_simple_object(type, formatted_time);
438 }
439
440 /*
441 * Defined in header.
442 */
443 void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private)
444 {
445 int oid;
446
447 switch (type)
448 {
449 case ASN1_OID:
450 oid = asn1_known_oid(object);
451 if (oid != OID_UNKNOWN)
452 {
453 DBG2(" '%s'", oid_names[oid].name);
454 return;
455 }
456 break;
457 case ASN1_UTF8STRING:
458 case ASN1_IA5STRING:
459 case ASN1_PRINTABLESTRING:
460 case ASN1_T61STRING:
461 case ASN1_VISIBLESTRING:
462 DBG2(" '%.*s'", (int)object.len, object.ptr);
463 return;
464 case ASN1_UTCTIME:
465 case ASN1_GENERALIZEDTIME:
466 {
467 time_t time = asn1_to_time(&object, type);
468
469 DBG2(" '%T'", &time, TRUE);
470 }
471 return;
472 default:
473 break;
474 }
475 if (private)
476 {
477 DBG4("%B", &object);
478 }
479 else
480 {
481 DBG3("%B", &object);
482 }
483 }
484
485 /**
486 * parse an ASN.1 simple type
487 */
488 bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name)
489 {
490 size_t len;
491
492 /* an ASN.1 object must possess at least a tag and length field */
493 if (object->len < 2)
494 {
495 DBG2("L%d - %s: ASN.1 object smaller than 2 octets", level, name);
496 return FALSE;
497 }
498
499 if (*object->ptr != type)
500 {
501 DBG2("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
502 level, name, type, *object->ptr);
503 return FALSE;
504 }
505
506 len = asn1_length(object);
507
508 if (len == ASN1_INVALID_LENGTH || object->len < len)
509 {
510 DBG2("L%d - %s: length of ASN.1 object invalid or too large",
511 level, name);
512 return FALSE;
513 }
514
515 DBG2("L%d - %s:", level, name);
516 asn1_debug_simple_object(*object, type, FALSE);
517 return TRUE;
518 }
519
520 /**
521 * ASN.1 definition of an algorithmIdentifier
522 */
523 static const asn1Object_t algorithmIdentifierObjects[] = {
524 { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
525 { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
526 { 1, "parameters", ASN1_EOC, ASN1_RAW|ASN1_OPT }, /* 2 */
527 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
528 { 0, "exit", ASN1_EOC, ASN1_EXIT }
529 };
530 #define ALGORITHM_ID_ALG 1
531 #define ALGORITHM_ID_PARAMETERS 2
532
533 /*
534 * Defined in header
535 */
536 int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
537 {
538 asn1_parser_t *parser;
539 chunk_t object;
540 int objectID;
541 int alg = OID_UNKNOWN;
542
543 parser = asn1_parser_create(algorithmIdentifierObjects, blob);
544 parser->set_top_level(parser, level0);
545
546 while (parser->iterate(parser, &objectID, &object))
547 {
548 switch (objectID)
549 {
550 case ALGORITHM_ID_ALG:
551 alg = asn1_known_oid(object);
552 break;
553 case ALGORITHM_ID_PARAMETERS:
554 if (parameters != NULL)
555 {
556 *parameters = object;
557 }
558 break;
559 default:
560 break;
561 }
562 }
563 parser->destroy(parser);
564 return alg;
565 }
566
567 /*
568 * tests if a blob contains a valid ASN.1 set or sequence
569 */
570 bool is_asn1(chunk_t blob)
571 {
572 u_int len;
573 u_char tag = *blob.ptr;
574
575 if (tag != ASN1_SEQUENCE && tag != ASN1_SET)
576 {
577 DBG2(" file content is not binary ASN.1");
578 return FALSE;
579 }
580
581 len = asn1_length(&blob);
582
583 /* exact match */
584 if (len == blob.len)
585 {
586 return TRUE;
587 }
588
589 /* some websites append a surplus newline character to the blob */
590 if (len + 1 == blob.len && *(blob.ptr + len) == '\n')
591 {
592 return TRUE;
593 }
594
595 DBG2(" file size does not match ASN.1 coded length");
596 return FALSE;
597 }
598
599 /*
600 * Defined in header.
601 */
602 bool asn1_is_printablestring(chunk_t str)
603 {
604 const char printablestring_charset[] =
605 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
606 u_int i;
607
608 for (i = 0; i < str.len; i++)
609 {
610 if (strchr(printablestring_charset, str.ptr[i]) == NULL)
611 return FALSE;
612 }
613 return TRUE;
614 }
615
616 /**
617 * codes ASN.1 lengths up to a size of 16'777'215 bytes
618 */
619 static void asn1_code_length(size_t length, chunk_t *code)
620 {
621 if (length < 128)
622 {
623 code->ptr[0] = length;
624 code->len = 1;
625 }
626 else if (length < 256)
627 {
628 code->ptr[0] = 0x81;
629 code->ptr[1] = (u_char) length;
630 code->len = 2;
631 }
632 else if (length < 65536)
633 {
634 code->ptr[0] = 0x82;
635 code->ptr[1] = length >> 8;
636 code->ptr[2] = length & 0x00ff;
637 code->len = 3;
638 }
639 else
640 {
641 code->ptr[0] = 0x83;
642 code->ptr[1] = length >> 16;
643 code->ptr[2] = (length >> 8) & 0x00ff;
644 code->ptr[3] = length & 0x0000ff;
645 code->len = 4;
646 }
647 }
648
649 /**
650 * build an empty asn.1 object with tag and length fields already filled in
651 */
652 u_char* asn1_build_object(chunk_t *object, asn1_t type, size_t datalen)
653 {
654 u_char length_buf[4];
655 chunk_t length = { length_buf, 0 };
656 u_char *pos;
657
658 /* code the asn.1 length field */
659 asn1_code_length(datalen, &length);
660
661 /* allocate memory for the asn.1 TLV object */
662 object->len = 1 + length.len + datalen;
663 object->ptr = malloc(object->len);
664
665 /* set position pointer at the start of the object */
666 pos = object->ptr;
667
668 /* copy the asn.1 tag field and advance the pointer */
669 *pos++ = type;
670
671 /* copy the asn.1 length field and advance the pointer */
672 memcpy(pos, length.ptr, length.len);
673 pos += length.len;
674
675 return pos;
676 }
677
678 /**
679 * Build a simple ASN.1 object
680 */
681 chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
682 {
683 chunk_t object;
684
685 u_char *pos = asn1_build_object(&object, tag, content.len);
686 memcpy(pos, content.ptr, content.len);
687 pos += content.len;
688
689 return object;
690 }
691
692 /**
693 * Build an ASN.1 BITSTRING object
694 */
695 chunk_t asn1_bitstring(const char *mode, chunk_t content)
696 {
697 chunk_t object;
698 u_char *pos = asn1_build_object(&object, ASN1_BIT_STRING, 1 + content.len);
699
700 *pos++ = 0x00;
701 memcpy(pos, content.ptr, content.len);
702 if (*mode == 'm')
703 {
704 free(content.ptr);
705 }
706 return object;
707 }
708
709 /**
710 * Build an ASN.1 object from a variable number of individual chunks.
711 * Depending on the mode, chunks either are moved ('m') or copied ('c').
712 */
713 chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
714 {
715 chunk_t construct;
716 va_list chunks;
717 u_char *pos;
718 int i;
719 int count = strlen(mode);
720
721 /* sum up lengths of individual chunks */
722 va_start(chunks, mode);
723 construct.len = 0;
724 for (i = 0; i < count; i++)
725 {
726 chunk_t ch = va_arg(chunks, chunk_t);
727 construct.len += ch.len;
728 }
729 va_end(chunks);
730
731 /* allocate needed memory for construct */
732 pos = asn1_build_object(&construct, type, construct.len);
733
734 /* copy or move the chunks */
735 va_start(chunks, mode);
736 for (i = 0; i < count; i++)
737 {
738 chunk_t ch = va_arg(chunks, chunk_t);
739
740 memcpy(pos, ch.ptr, ch.len);
741 pos += ch.len;
742
743 if (*mode++ == 'm')
744 {
745 free(ch.ptr);
746 }
747 }
748 va_end(chunks);
749
750 return construct;
751 }
752
753 /**
754 * ASN.1 definition of time
755 */
756 static const asn1Object_t timeObjects[] = {
757 { 0, "utcTime", ASN1_UTCTIME, ASN1_OPT|ASN1_BODY }, /* 0 */
758 { 0, "end opt", ASN1_EOC, ASN1_END }, /* 1 */
759 { 0, "generalizeTime", ASN1_GENERALIZEDTIME, ASN1_OPT|ASN1_BODY }, /* 2 */
760 { 0, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
761 { 0, "exit", ASN1_EOC, ASN1_EXIT }
762 };
763 #define TIME_UTC 0
764 #define TIME_GENERALIZED 2
765
766 /**
767 * extracts and converts a UTCTIME or GENERALIZEDTIME object
768 */
769 time_t asn1_parse_time(chunk_t blob, int level0)
770 {
771 asn1_parser_t *parser;
772 chunk_t object;
773 int objectID;
774 time_t utc_time = 0;
775
776 parser= asn1_parser_create(timeObjects, blob);
777 parser->set_top_level(parser, level0);
778
779 while (parser->iterate(parser, &objectID, &object))
780 {
781 if (objectID == TIME_UTC || objectID == TIME_GENERALIZED)
782 {
783 utc_time = asn1_to_time(&object, (objectID == TIME_UTC)
784 ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME);
785 }
786 }
787 parser->destroy(parser);
788 return utc_time;
789 }