2 * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur
3 * Copyright (C) 2006 Martin Will, Hochschule fuer Technik Rapperswil
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>.
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
22 #include <utils/logger_manager.h>
24 /* Names of the months */
25 static const char* months
[] = {
26 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
27 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
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 };
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
);
39 /* some popular algorithmIdentifiers */
41 static u_char ASN1_md5_id_str
[] = {
43 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05,
47 static u_char ASN1_sha1_id_str
[] = {
49 0x06, 0x05, 0x2B, 0x0E,0x03, 0x02, 0x1A,
53 static u_char ASN1_md5WithRSA_id_str
[] = {
55 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04,
59 static u_char ASN1_sha1WithRSA_id_str
[] = {
61 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
65 static u_char ASN1_rsaEncryption_id_str
[] = {
67 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
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
);
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 */
84 #define ALGORITHM_ID_ALG 1
85 #define ALGORITHM_ID_PARAMETERS 2
86 #define ALGORITHM_ID_ROOF 3
88 static logger_t
*logger
= NULL
;
91 * initializes the ASN.1 logger
93 static void asn1_init_logger(void)
96 logger
= logger_manager
->get_logger(logger_manager
, ASN1
);
100 * return the ASN.1 encoded algorithm identifier
102 chunk_t
asn1_algorithmIdentifier(int oid
)
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
;
117 return CHUNK_INITIALIZER
;
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
125 int known_oid(chunk_t object
)
131 if (oid_names
[oid
].octet
== *object
.ptr
)
133 if (--object
.len
== 0 || oid_names
[oid
].down
== 0)
135 return oid
; /* found terminal symbol */
139 object
.ptr
++; oid
++; /* advance to next hex octet */
144 if (oid_names
[oid
].next
)
145 oid
= oid_names
[oid
].next
;
154 * Decodes the length in bytes of an ASN.1 object
156 u_int
asn1_length(chunk_t
*blob
)
161 /* advance from tag field on to length field */
165 /* read first octet of length field */
170 {/* single length octet */
174 /* composite length, determine number of length octets */
179 logger
->log(logger
, ERROR
|LEVEL1
, "number of length octets is larger than ASN.1 object");
180 return ASN1_INVALID_LENGTH
;
185 logger
->log(logger
, ERROR
|LEVEL1
, "number of length octets is larger than limit of %d octets",
187 return ASN1_INVALID_LENGTH
;
194 len
= 256*len
+ *blob
->ptr
++;
201 * determines if a character string is of type ASN.1 printableString
203 bool is_printablestring(chunk_t str
)
205 const char printablestring_charset
[] =
206 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
209 for (i
= 0; i
< str
.len
; i
++)
211 if (strchr(printablestring_charset
, str
.ptr
[i
]) == NULL
)
218 * Display a date either in local or UTC time
219 * TODO: Does not seem to be thread safe
221 char* timetoa(const time_t *time
, bool utc
)
226 sprintf(buf
, "--- -- --:--:--%s----", (utc
)?
" UTC ":" ");
229 struct tm
*t
= (utc
)?
gmtime(time
) : localtime(time
);
230 sprintf(buf
, "%s %02d %02d:%02d:%02d%s%04d",
231 months
[t
->tm_mon
], t
->tm_mday
, t
->tm_hour
, t
->tm_min
, t
->tm_sec
,
232 (utc
)?
" UTC ":" ", t
->tm_year
+ 1900);
238 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
240 time_t asn1totime(const chunk_t
*utctime
, asn1_t type
)
246 if ((eot
= memchr(utctime
->ptr
, 'Z', utctime
->len
)) != NULL
)
248 tz_offset
= 0; /* Zulu time with a zero time zone offset */
250 else if ((eot
= memchr(utctime
->ptr
, '+', utctime
->len
)) != NULL
)
254 sscanf(eot
+1, "%2d%2d", &tz_hour
, &tz_min
);
255 tz_offset
= 3600*tz_hour
+ 60*tz_min
; /* positive time zone offset */
257 else if ((eot
= memchr(utctime
->ptr
, '-', utctime
->len
)) != NULL
)
261 sscanf(eot
+1, "%2d%2d", &tz_hour
, &tz_min
);
262 tz_offset
= -3600*tz_hour
- 60*tz_min
; /* negative time zone offset */
266 return 0; /* error in time format */
270 const char* format
= (type
== ASN1_UTCTIME
)?
"%2d%2d%2d%2d%2d":
273 sscanf(utctime
->ptr
, format
, &t
.tm_year
, &t
.tm_mon
, &t
.tm_mday
,
274 &t
.tm_hour
, &t
.tm_min
);
277 /* is there a seconds field? */
278 if ((eot
- utctime
->ptr
) == ((type
== ASN1_UTCTIME
)?
12:14))
280 sscanf(eot
-2, "%2d", &t
.tm_sec
);
287 /* representation of year */
288 if (t
.tm_year
>= 1900)
292 else if (t
.tm_year
>= 100)
296 else if (t
.tm_year
< 50)
301 /* representation of month 0..11*/
304 /* set daylight saving time to off */
307 /* compensate timezone */
309 return mktime(&t
) - timezone
- tz_offset
;
313 * Initializes the internal context of the ASN.1 parser
315 void asn1_init(asn1_ctx_t
*ctx
, chunk_t blob
, u_int level0
, bool implicit
)
319 ctx
->blobs
[0] = blob
;
320 ctx
->level0
= level0
;
321 ctx
->implicit
= implicit
;
322 memset(ctx
->loopAddr
, '\0', sizeof(ctx
->loopAddr
));
326 * print the value of an ASN.1 simple object
328 static void debug_asn1_simple_object(chunk_t object
, asn1_t type
)
336 oid
= known_oid(object
);
337 if (oid
!= OID_UNKNOWN
)
339 logger
->log(logger
, CONTROL
|LEVEL1
, " '%s'", oid_names
[oid
].name
);
343 case ASN1_UTF8STRING
:
345 case ASN1_PRINTABLESTRING
:
347 case ASN1_VISIBLESTRING
:
348 logger
->log(logger
, CONTROL
|LEVEL1
, " '%.*s'", (int)object
.len
, object
.ptr
);
351 case ASN1_GENERALIZEDTIME
:
352 time
= asn1totime(&object
, type
);
353 logger
->log(logger
, CONTROL
|LEVEL1
, " '%s'", timetoa(&time
, TRUE
));
358 logger
->log_chunk(logger
, RAW
|LEVEL1
, "", object
);
362 * Parses and extracts the next ASN.1 object
364 bool extract_object(asn1Object_t
const *objects
, u_int
*objectID
, chunk_t
*object
, u_int
*level
, asn1_ctx_t
*ctx
)
366 asn1Object_t obj
= objects
[*objectID
];
371 *object
= CHUNK_INITIALIZER
;
373 if (obj
.flags
& ASN1_END
) /* end of loop or option found */
375 if (ctx
->loopAddr
[obj
.level
] && ctx
->blobs
[obj
.level
+1].len
> 0)
377 *objectID
= ctx
->loopAddr
[obj
.level
]; /* another iteration */
378 obj
= objects
[*objectID
];
382 ctx
->loopAddr
[obj
.level
] = 0; /* exit loop or option*/
387 *level
= ctx
->level0
+ obj
.level
;
388 blob
= ctx
->blobs
+ obj
.level
;
390 start_ptr
= blob
->ptr
;
392 /* handle ASN.1 defaults values */
393 if ((obj
.flags
& ASN1_DEF
) && (blob
->len
== 0 || *start_ptr
!= obj
.type
) )
395 /* field is missing */
396 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", *level
, obj
.name
);
397 if (obj
.type
& ASN1_CONSTRUCTED
)
399 (*objectID
)++ ; /* skip context-specific tag */
404 /* handle ASN.1 options */
406 if ((obj
.flags
& ASN1_OPT
)
407 && (blob
->len
== 0 || *start_ptr
!= obj
.type
))
409 /* advance to end of missing option field */
412 while (!((objects
[*objectID
].flags
& ASN1_END
)
413 && (objects
[*objectID
].level
== obj
.level
)));
417 /* an ASN.1 object must possess at least a tag and length field */
421 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN.1 object smaller than 2 octets",
426 blob1
->len
= asn1_length(blob
);
428 if (blob1
->len
== ASN1_INVALID_LENGTH
|| blob
->len
< blob1
->len
)
430 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: length of ASN.1 object invalid or too large",
435 blob1
->ptr
= blob
->ptr
;
436 blob
->ptr
+= blob1
->len
;
437 blob
->len
-= blob1
->len
;
439 /* return raw ASN.1 object without prior type checking */
441 if (obj
.flags
& ASN1_RAW
)
443 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", *level
, obj
.name
);
444 object
->ptr
= start_ptr
;
445 object
->len
= (size_t)(blob
->ptr
- start_ptr
);
449 if (*start_ptr
!= obj
.type
&& !(ctx
->implicit
&& *objectID
== 0))
451 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
452 *level
, obj
.name
, obj
.type
, *start_ptr
);
453 logger
->log_bytes(logger
, RAW
|LEVEL1
, "", start_ptr
, (u_int
)(blob
->ptr
- start_ptr
));
457 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", ctx
->level0
+obj
.level
, obj
.name
);
459 /* In case of "SEQUENCE OF" or "SET OF" start a loop */
460 if (obj
.flags
& ASN1_LOOP
)
464 /* at least one item, start the loop */
465 ctx
->loopAddr
[obj
.level
] = *objectID
+ 1;
469 /* no items, advance directly to end of loop */
472 while (!((objects
[*objectID
].flags
& ASN1_END
)
473 && (objects
[*objectID
].level
== obj
.level
)));
478 if (obj
.flags
& ASN1_OBJ
)
480 object
->ptr
= start_ptr
;
481 object
->len
= (size_t)(blob
->ptr
- start_ptr
);
482 logger
->log_chunk(logger
, RAW
|LEVEL1
, "", *object
);
484 else if (obj
.flags
& ASN1_BODY
)
487 debug_asn1_simple_object(*object
, obj
.type
);
493 * parse an ASN.1 simple type
495 bool parse_asn1_simple_object(chunk_t
*object
, asn1_t type
, u_int level
, const char* name
)
499 /* an ASN.1 object must possess at least a tag and length field */
502 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN.1 object smaller than 2 octets",
507 if (*object
->ptr
!= type
)
509 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
510 level
, name
, type
, *object
->ptr
);
514 len
= asn1_length(object
);
516 if (len
== ASN1_INVALID_LENGTH
|| object
->len
< len
)
518 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: length of ASN.1 object invalid or too large",
523 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", level
, name
);
524 debug_asn1_simple_object(*object
, type
);
529 * extracts an algorithmIdentifier
531 int parse_algorithmIdentifier(chunk_t blob
, int level0
, chunk_t
*parameters
)
536 int alg
= OID_UNKNOWN
;
539 asn1_init(&ctx
, blob
, level0
, FALSE
);
541 while (objectID
< ALGORITHM_ID_ROOF
)
543 if (!extract_object(algorithmIdentifierObjects
, &objectID
, &object
, &level
, &ctx
))
548 case ALGORITHM_ID_ALG
:
549 alg
= known_oid(object
);
551 case ALGORITHM_ID_PARAMETERS
:
552 if (parameters
!= NULL
)
553 *parameters
= object
;
564 * tests if a blob contains a valid ASN.1 set or sequence
566 bool is_asn1(chunk_t blob
)
569 u_char tag
= *blob
.ptr
;
573 if (tag
!= ASN1_SEQUENCE
&& tag
!= ASN1_SET
)
575 logger
->log(logger
, ERROR
|LEVEL2
, " file content is not binary ASN.1");
578 len
= asn1_length(&blob
);
581 logger
->log(logger
, ERROR
|LEVEL2
, " file size does not match ASN.1 coded length");
588 * codes ASN.1 lengths up to a size of 16'777'215 bytes
590 void code_asn1_length(size_t length
, chunk_t
*code
)
594 code
->ptr
[0] = length
;
597 else if (length
< 256)
600 code
->ptr
[1] = (u_char
) length
;
603 else if (length
< 65536)
606 code
->ptr
[1] = length
>> 8;
607 code
->ptr
[2] = length
& 0x00ff;
613 code
->ptr
[1] = length
>> 16;
614 code
->ptr
[2] = (length
>> 8) & 0x00ff;
615 code
->ptr
[3] = length
& 0x0000ff;
621 * build an empty asn.1 object with tag and length fields already filled in
623 u_char
* build_asn1_object(chunk_t
*object
, asn1_t type
, size_t datalen
)
625 u_char length_buf
[4];
626 chunk_t length
= { length_buf
, 0 };
629 /* code the asn.1 length field */
630 code_asn1_length(datalen
, &length
);
632 /* allocate memory for the asn.1 TLV object */
633 object
->len
= 1 + length
.len
+ datalen
;
634 object
->ptr
= malloc(object
->len
);
636 /* set position pointer at the start of the object */
639 /* copy the asn.1 tag field and advance the pointer */
642 /* copy the asn.1 length field and advance the pointer */
643 memcpy(pos
, length
.ptr
, length
.len
);
650 * build a simple ASN.1 object
652 chunk_t
asn1_simple_object(asn1_t tag
, chunk_t content
)
656 u_char
*pos
= build_asn1_object(&object
, tag
, content
.len
);
657 memcpy(pos
, content
.ptr
, content
.len
);
664 * Build an ASN.1 object from a variable number of individual chunks.
665 * Depending on the mode, chunks either are moved ('m') or copied ('c').
667 chunk_t
asn1_wrap(asn1_t type
, const char *mode
, ...)
673 int count
= strlen(mode
);
675 /* sum up lengths of individual chunks */
676 va_start(chunks
, mode
);
678 for (i
= 0; i
< count
; i
++)
680 chunk_t ch
= va_arg(chunks
, chunk_t
);
681 construct
.len
+= ch
.len
;
685 /* allocate needed memory for construct */
686 pos
= build_asn1_object(&construct
, type
, construct
.len
);
688 /* copy or move the chunks */
689 va_start(chunks
, mode
);
690 for (i
= 0; i
< count
; i
++)
692 chunk_t ch
= va_arg(chunks
, chunk_t
);
697 memcpy(pos
, ch
.ptr
, ch
.len
);
703 memcpy(pos
, ch
.ptr
, ch
.len
);
713 * convert a MP integer into a DER coded ASN.1 object
715 chunk_t
asn1_integer_from_mpz(const mpz_t value
)
717 size_t bits
= mpz_sizeinbase(value
, 2); /* size in bits */
719 n
.len
= 1 + bits
/ 8; /* size in bytes */
720 n
.ptr
= mpz_export(NULL
, NULL
, 1, n
.len
, 1, 0, value
);
722 return asn1_wrap(ASN1_INTEGER
, "m", n
);
726 * convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
728 chunk_t
timetoasn1(const time_t *time
, asn1_t type
)
732 char buf
[TIMETOA_BUF
];
733 chunk_t formatted_time
;
734 struct tm
*t
= gmtime(time
);
736 if (type
== ASN1_GENERALIZEDTIME
)
738 format
= "%04d%02d%02d%02d%02d%02dZ";
741 else /* ASN1_UTCTIME */
743 format
= "%02d%02d%02d%02d%02d%02dZ";
744 offset
= (t
->tm_year
< 100)?
0 : -100;
746 sprintf(buf
, format
, t
->tm_year
+ offset
, t
->tm_mon
+ 1, t
->tm_mday
747 , t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
748 formatted_time
.ptr
= buf
;
749 formatted_time
.len
= strlen(buf
);
750 return asn1_simple_object(type
, formatted_time
);