- applied patch from andreas, which allows certificate listing via stroke
[strongswan.git] / src / libstrongswan / asn1 / asn1.c
1 /* Simple ASN.1 parser
2 * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur
3 * Copyright (C) 2006 Martin Will, Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19
20 #include "asn1.h"
21
22 #include <utils/logger_manager.h>
23
24 /* Names of the months */
25 static const char* months[] = {
26 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
27 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
28 };
29
30 /* some common prefabricated ASN.1 constants */
31 static u_char ASN1_INTEGER_0_str[] = { 0x02, 0x00 };
32 static u_char ASN1_INTEGER_1_str[] = { 0x02, 0x01, 0x01 };
33 static u_char ASN1_INTEGER_2_str[] = { 0x02, 0x01, 0x02 };
34
35 const chunk_t ASN1_INTEGER_0 = chunk_from_buf(ASN1_INTEGER_0_str);
36 const chunk_t ASN1_INTEGER_1 = chunk_from_buf(ASN1_INTEGER_1_str);
37 const chunk_t ASN1_INTEGER_2 = chunk_from_buf(ASN1_INTEGER_2_str);
38
39 /* some popular algorithmIdentifiers */
40
41 static u_char ASN1_md5_id_str[] = {
42 0x30, 0x0C,
43 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05,
44 0x05, 0x00
45 };
46
47 static u_char ASN1_sha1_id_str[] = {
48 0x30, 0x09,
49 0x06, 0x05, 0x2B, 0x0E,0x03, 0x02, 0x1A,
50 0x05, 0x00
51 };
52
53 static u_char ASN1_md5WithRSA_id_str[] = {
54 0x30, 0x0D,
55 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04,
56 0x05, 0x00
57 };
58
59 static u_char ASN1_sha1WithRSA_id_str[] = {
60 0x30, 0x0D,
61 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
62 0x05, 0x00
63 };
64
65 static u_char ASN1_rsaEncryption_id_str[] = {
66 0x30, 0x0D,
67 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
68 0x05, 0x00
69 };
70
71 const chunk_t ASN1_md5_id = chunk_from_buf(ASN1_md5_id_str);
72 const chunk_t ASN1_sha1_id = chunk_from_buf(ASN1_sha1_id_str);
73 const chunk_t ASN1_rsaEncryption_id = chunk_from_buf(ASN1_rsaEncryption_id_str);
74 const chunk_t ASN1_md5WithRSA_id = chunk_from_buf(ASN1_md5WithRSA_id_str);
75 const chunk_t ASN1_sha1WithRSA_id = chunk_from_buf(ASN1_sha1WithRSA_id_str);
76
77 /* ASN.1 definiton of an algorithmIdentifier */
78 static const asn1Object_t algorithmIdentifierObjects[] = {
79 { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
80 { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */
81 { 1, "parameters", ASN1_EOC, ASN1_RAW } /* 2 */
82 };
83
84 #define ALGORITHM_ID_ALG 1
85 #define ALGORITHM_ID_PARAMETERS 2
86 #define ALGORITHM_ID_ROOF 3
87
88 static logger_t *logger = NULL;
89
90 /**
91 * initializes the ASN.1 logger
92 */
93 static void asn1_init_logger(void)
94 {
95 if (logger == NULL)
96 logger = logger_manager->get_logger(logger_manager, ASN1);
97 }
98
99 /**
100 * return the ASN.1 encoded algorithm identifier
101 */
102 chunk_t asn1_algorithmIdentifier(int oid)
103 {
104 switch (oid)
105 {
106 case OID_RSA_ENCRYPTION:
107 return ASN1_rsaEncryption_id;
108 case OID_MD5_WITH_RSA:
109 return ASN1_md5WithRSA_id;
110 case OID_SHA1_WITH_RSA:
111 return ASN1_sha1WithRSA_id;
112 case OID_MD5:
113 return ASN1_md5_id;
114 case OID_SHA1:
115 return ASN1_sha1_id;
116 default:
117 return CHUNK_INITIALIZER;
118 }
119 }
120
121 /**
122 * If the oid is listed in the oid_names table then the corresponding
123 * position in the oid_names table is returned otherwise -1 is returned
124 */
125 int known_oid(chunk_t object)
126 {
127 int oid = 0;
128
129 while (object.len)
130 {
131 if (oid_names[oid].octet == *object.ptr)
132 {
133 if (--object.len == 0 || oid_names[oid].down == 0)
134 {
135 return oid; /* found terminal symbol */
136 }
137 else
138 {
139 object.ptr++; oid++; /* advance to next hex octet */
140 }
141 }
142 else
143 {
144 if (oid_names[oid].next)
145 oid = oid_names[oid].next;
146 else
147 return OID_UNKNOWN;
148 }
149 }
150 return -1;
151 }
152
153 /**
154 * Decodes the length in bytes of an ASN.1 object
155 */
156 u_int asn1_length(chunk_t *blob)
157 {
158 u_char n;
159 size_t len;
160
161 /* advance from tag field on to length field */
162 blob->ptr++;
163 blob->len--;
164
165 /* read first octet of length field */
166 n = *blob->ptr++;
167 blob->len--;
168
169 if ((n & 0x80) == 0)
170 {/* single length octet */
171 return n;
172 }
173
174 /* composite length, determine number of length octets */
175 n &= 0x7f;
176
177 if (n > blob->len)
178 {
179 logger->log(logger, ERROR|LEVEL1, "number of length octets is larger than ASN.1 object");
180 return ASN1_INVALID_LENGTH;
181 }
182
183 if (n > sizeof(len))
184 {
185 logger->log(logger, ERROR|LEVEL1, "number of length octets is larger than limit of %d octets",
186 (int)sizeof(len));
187 return ASN1_INVALID_LENGTH;
188 }
189
190 len = 0;
191
192 while (n-- > 0)
193 {
194 len = 256*len + *blob->ptr++;
195 blob->len--;
196 }
197 return len;
198 }
199
200 /**
201 * determines if a character string is of type ASN.1 printableString
202 */
203 bool is_printablestring(chunk_t str)
204 {
205 const char printablestring_charset[] =
206 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
207 u_int i;
208
209 for (i = 0; i < str.len; i++)
210 {
211 if (strchr(printablestring_charset, str.ptr[i]) == NULL)
212 return FALSE;
213 }
214 return TRUE;
215 }
216
217 /**
218 * Display a date either in local or UTC time
219 */
220 void timetoa(char *buf, size_t buflen, const time_t *time, bool utc)
221 {
222 if (*time == 0)
223 snprintf(buf, buflen, "--- -- --:--:--%s----", (utc)?" UTC ":" ");
224 else
225 {
226 struct tm *t = (utc)? gmtime(time) : localtime(time);
227
228 snprintf(buf, buflen, "%s %02d %02d:%02d:%02d%s%04d",
229 months[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
230 (utc)?" UTC ":" ", t->tm_year + 1900);
231 }
232 }
233
234 /**
235 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
236 */
237 time_t asn1totime(const chunk_t *utctime, asn1_t type)
238 {
239 struct tm t;
240 time_t tz_offset;
241 u_char *eot = NULL;
242
243 if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
244 {
245 tz_offset = 0; /* Zulu time with a zero time zone offset */
246 }
247 else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
248 {
249 int tz_hour, tz_min;
250
251 sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min);
252 tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */
253 }
254 else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
255 {
256 int tz_hour, tz_min;
257
258 sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min);
259 tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */
260 }
261 else
262 {
263 return 0; /* error in time format */
264 }
265
266 {
267 const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
268 "%4d%2d%2d%2d%2d";
269
270 sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday,
271 &t.tm_hour, &t.tm_min);
272 }
273
274 /* is there a seconds field? */
275 if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
276 {
277 sscanf(eot-2, "%2d", &t.tm_sec);
278 }
279 else
280 {
281 t.tm_sec = 0;
282 }
283
284 /* representation of year */
285 if (t.tm_year >= 1900)
286 {
287 t.tm_year -= 1900;
288 }
289 else if (t.tm_year >= 100)
290 {
291 return 0;
292 }
293 else if (t.tm_year < 50)
294 {
295 t.tm_year += 100;
296 }
297
298 /* representation of month 0..11*/
299 t.tm_mon--;
300
301 /* set daylight saving time to off */
302 t.tm_isdst = 0;
303
304 /* compensate timezone */
305
306 return mktime(&t) - timezone - tz_offset;
307 }
308
309 /**
310 * Initializes the internal context of the ASN.1 parser
311 */
312 void asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0, bool implicit)
313 {
314 asn1_init_logger();
315
316 ctx->blobs[0] = blob;
317 ctx->level0 = level0;
318 ctx->implicit = implicit;
319 memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr));
320 }
321
322 /**
323 * print the value of an ASN.1 simple object
324 */
325 static void debug_asn1_simple_object(chunk_t object, asn1_t type)
326 {
327 int oid;
328 time_t time;
329
330 switch (type)
331 {
332 case ASN1_OID:
333 oid = known_oid(object);
334 if (oid != OID_UNKNOWN)
335 {
336 logger->log(logger, CONTROL|LEVEL1, " '%s'", oid_names[oid].name);
337 return;
338 }
339 break;
340 case ASN1_UTF8STRING:
341 case ASN1_IA5STRING:
342 case ASN1_PRINTABLESTRING:
343 case ASN1_T61STRING:
344 case ASN1_VISIBLESTRING:
345 logger->log(logger, CONTROL|LEVEL1, " '%.*s'", (int)object.len, object.ptr);
346 return;
347 case ASN1_UTCTIME:
348 case ASN1_GENERALIZEDTIME:
349 {
350 char buf[TIMETOA_BUF];
351 time_t time = asn1totime(&object, type);
352
353 timetoa(buf, TIMETOA_BUF, &time, TRUE);
354 logger->log(logger, CONTROL|LEVEL1, " '%s'", buf);
355 }
356 return;
357 default:
358 break;
359 }
360 logger->log_chunk(logger, RAW|LEVEL1, "", object);
361 }
362
363 /**
364 * Parses and extracts the next ASN.1 object
365 */
366 bool extract_object(asn1Object_t const *objects, u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx)
367 {
368 asn1Object_t obj = objects[*objectID];
369 chunk_t *blob;
370 chunk_t *blob1;
371 u_char *start_ptr;
372
373 *object = CHUNK_INITIALIZER;
374
375 if (obj.flags & ASN1_END) /* end of loop or option found */
376 {
377 if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0)
378 {
379 *objectID = ctx->loopAddr[obj.level]; /* another iteration */
380 obj = objects[*objectID];
381 }
382 else
383 {
384 ctx->loopAddr[obj.level] = 0; /* exit loop or option*/
385 return TRUE;
386 }
387 }
388
389 *level = ctx->level0 + obj.level;
390 blob = ctx->blobs + obj.level;
391 blob1 = blob + 1;
392 start_ptr = blob->ptr;
393
394 /* handle ASN.1 defaults values */
395 if ((obj.flags & ASN1_DEF) && (blob->len == 0 || *start_ptr != obj.type) )
396 {
397 /* field is missing */
398 logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", *level, obj.name);
399 if (obj.type & ASN1_CONSTRUCTED)
400 {
401 (*objectID)++ ; /* skip context-specific tag */
402 }
403 return TRUE;
404 }
405
406 /* handle ASN.1 options */
407
408 if ((obj.flags & ASN1_OPT)
409 && (blob->len == 0 || *start_ptr != obj.type))
410 {
411 /* advance to end of missing option field */
412 do
413 (*objectID)++;
414 while (!((objects[*objectID].flags & ASN1_END)
415 && (objects[*objectID].level == obj.level)));
416 return TRUE;
417 }
418
419 /* an ASN.1 object must possess at least a tag and length field */
420
421 if (blob->len < 2)
422 {
423 logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN.1 object smaller than 2 octets",
424 *level, obj.name);
425 return FALSE;
426 }
427
428 blob1->len = asn1_length(blob);
429
430 if (blob1->len == ASN1_INVALID_LENGTH || blob->len < blob1->len)
431 {
432 logger->log(logger, ERROR|LEVEL1, "L%d - %s: length of ASN.1 object invalid or too large",
433 *level, obj.name);
434 return FALSE;
435 }
436
437 blob1->ptr = blob->ptr;
438 blob->ptr += blob1->len;
439 blob->len -= blob1->len;
440
441 /* return raw ASN.1 object without prior type checking */
442
443 if (obj.flags & ASN1_RAW)
444 {
445 logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", *level, obj.name);
446 object->ptr = start_ptr;
447 object->len = (size_t)(blob->ptr - start_ptr);
448 return TRUE;
449 }
450
451 if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0))
452 {
453 logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
454 *level, obj.name, obj.type, *start_ptr);
455 logger->log_bytes(logger, RAW|LEVEL1, "", start_ptr, (u_int)(blob->ptr - start_ptr));
456 return FALSE;
457 }
458
459 logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", ctx->level0+obj.level, obj.name);
460
461 /* In case of "SEQUENCE OF" or "SET OF" start a loop */
462 if (obj.flags & ASN1_LOOP)
463 {
464 if (blob1->len > 0)
465 {
466 /* at least one item, start the loop */
467 ctx->loopAddr[obj.level] = *objectID + 1;
468 }
469 else
470 {
471 /* no items, advance directly to end of loop */
472 do
473 (*objectID)++;
474 while (!((objects[*objectID].flags & ASN1_END)
475 && (objects[*objectID].level == obj.level)));
476 return TRUE;
477 }
478 }
479
480 if (obj.flags & ASN1_OBJ)
481 {
482 object->ptr = start_ptr;
483 object->len = (size_t)(blob->ptr - start_ptr);
484 logger->log_chunk(logger, RAW|LEVEL1, "", *object);
485 }
486 else if (obj.flags & ASN1_BODY)
487 {
488 *object = *blob1;
489 debug_asn1_simple_object(*object, obj.type);
490 }
491 return TRUE;
492 }
493
494 /**
495 * parse an ASN.1 simple type
496 */
497 bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name)
498 {
499 size_t len;
500
501 /* an ASN.1 object must possess at least a tag and length field */
502 if (object->len < 2)
503 {
504 logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN.1 object smaller than 2 octets",
505 level, name);
506 return FALSE;
507 }
508
509 if (*object->ptr != type)
510 {
511 logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
512 level, name, type, *object->ptr);
513 return FALSE;
514 }
515
516 len = asn1_length(object);
517
518 if (len == ASN1_INVALID_LENGTH || object->len < len)
519 {
520 logger->log(logger, ERROR|LEVEL1, "L%d - %s: length of ASN.1 object invalid or too large",
521 level, name);
522 return FALSE;
523 }
524
525 logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", level, name);
526 debug_asn1_simple_object(*object, type);
527 return TRUE;
528 }
529
530 /**
531 * extracts an algorithmIdentifier
532 */
533 int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
534 {
535 asn1_ctx_t ctx;
536 chunk_t object;
537 u_int level;
538 int alg = OID_UNKNOWN;
539 int objectID = 0;
540
541 asn1_init(&ctx, blob, level0, FALSE);
542
543 while (objectID < ALGORITHM_ID_ROOF)
544 {
545 if (!extract_object(algorithmIdentifierObjects, &objectID, &object, &level, &ctx))
546 return OID_UNKNOWN;
547
548 switch (objectID)
549 {
550 case ALGORITHM_ID_ALG:
551 alg = known_oid(object);
552 break;
553 case ALGORITHM_ID_PARAMETERS:
554 if (parameters != NULL)
555 *parameters = object;
556 break;
557 default:
558 break;
559 }
560 objectID++;
561 }
562 return alg;
563 }
564
565 /*
566 * tests if a blob contains a valid ASN.1 set or sequence
567 */
568 bool is_asn1(chunk_t blob)
569 {
570 u_int len;
571 u_char tag = *blob.ptr;
572
573 asn1_init_logger();
574
575 if (tag != ASN1_SEQUENCE && tag != ASN1_SET)
576 {
577 logger->log(logger, ERROR|LEVEL2, " file content is not binary ASN.1");
578 return FALSE;
579 }
580 len = asn1_length(&blob);
581 if (len != blob.len)
582 {
583 logger->log(logger, ERROR|LEVEL2, " file size does not match ASN.1 coded length");
584 return FALSE;
585 }
586 return TRUE;
587 }
588
589 /**
590 * codes ASN.1 lengths up to a size of 16'777'215 bytes
591 */
592 void code_asn1_length(size_t length, chunk_t *code)
593 {
594 if (length < 128)
595 {
596 code->ptr[0] = length;
597 code->len = 1;
598 }
599 else if (length < 256)
600 {
601 code->ptr[0] = 0x81;
602 code->ptr[1] = (u_char) length;
603 code->len = 2;
604 }
605 else if (length < 65536)
606 {
607 code->ptr[0] = 0x82;
608 code->ptr[1] = length >> 8;
609 code->ptr[2] = length & 0x00ff;
610 code->len = 3;
611 }
612 else
613 {
614 code->ptr[0] = 0x83;
615 code->ptr[1] = length >> 16;
616 code->ptr[2] = (length >> 8) & 0x00ff;
617 code->ptr[3] = length & 0x0000ff;
618 code->len = 4;
619 }
620 }
621
622 /**
623 * build an empty asn.1 object with tag and length fields already filled in
624 */
625 u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen)
626 {
627 u_char length_buf[4];
628 chunk_t length = { length_buf, 0 };
629 u_char *pos;
630
631 /* code the asn.1 length field */
632 code_asn1_length(datalen, &length);
633
634 /* allocate memory for the asn.1 TLV object */
635 object->len = 1 + length.len + datalen;
636 object->ptr = malloc(object->len);
637
638 /* set position pointer at the start of the object */
639 pos = object->ptr;
640
641 /* copy the asn.1 tag field and advance the pointer */
642 *pos++ = type;
643
644 /* copy the asn.1 length field and advance the pointer */
645 memcpy(pos, length.ptr, length.len);
646 pos += length.len;
647
648 return pos;
649 }
650
651 /**
652 * build a simple ASN.1 object
653 */
654 chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
655 {
656 chunk_t object;
657
658 u_char *pos = build_asn1_object(&object, tag, content.len);
659 memcpy(pos, content.ptr, content.len);
660 pos += content.len;
661
662 return object;
663 }
664
665 /**
666 * Build an ASN.1 object from a variable number of individual chunks.
667 * Depending on the mode, chunks either are moved ('m') or copied ('c').
668 */
669 chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
670 {
671 chunk_t construct;
672 va_list chunks;
673 u_char *pos;
674 int i;
675 int count = strlen(mode);
676
677 /* sum up lengths of individual chunks */
678 va_start(chunks, mode);
679 construct.len = 0;
680 for (i = 0; i < count; i++)
681 {
682 chunk_t ch = va_arg(chunks, chunk_t);
683 construct.len += ch.len;
684 }
685 va_end(chunks);
686
687 /* allocate needed memory for construct */
688 pos = build_asn1_object(&construct, type, construct.len);
689
690 /* copy or move the chunks */
691 va_start(chunks, mode);
692 for (i = 0; i < count; i++)
693 {
694 chunk_t ch = va_arg(chunks, chunk_t);
695
696 switch (*mode++)
697 {
698 case 'm':
699 memcpy(pos, ch.ptr, ch.len);
700 pos += ch.len;
701 free(ch.ptr);
702 break;
703 case 'c':
704 default:
705 memcpy(pos, ch.ptr, ch.len);
706 pos += ch.len;
707 }
708 }
709 va_end(chunks);
710
711 return construct;
712 }
713
714 /**
715 * convert a MP integer into a DER coded ASN.1 object
716 */
717 chunk_t asn1_integer_from_mpz(const mpz_t value)
718 {
719 size_t bits = mpz_sizeinbase(value, 2); /* size in bits */
720 chunk_t n;
721 n.len = 1 + bits / 8; /* size in bytes */
722 n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, value);
723
724 return asn1_wrap(ASN1_INTEGER, "m", n);
725 }
726
727 /**
728 * convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
729 */
730 chunk_t timetoasn1(const time_t *time, asn1_t type)
731 {
732 int offset;
733 const char *format;
734 char buf[TIMETOA_BUF];
735 chunk_t formatted_time;
736 struct tm *t = gmtime(time);
737
738 if (type == ASN1_GENERALIZEDTIME)
739 {
740 format = "%04d%02d%02d%02d%02d%02dZ";
741 offset = 1900;
742 }
743 else /* ASN1_UTCTIME */
744 {
745 format = "%02d%02d%02d%02d%02d%02dZ";
746 offset = (t->tm_year < 100)? 0 : -100;
747 }
748 sprintf(buf, format, t->tm_year + offset, t->tm_mon + 1, t->tm_mday
749 , t->tm_hour, t->tm_min, t->tm_sec);
750 formatted_time.ptr = buf;
751 formatted_time.len = strlen(buf);
752 return asn1_simple_object(type, formatted_time);
753 }