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 static logger_t
*logger
;
26 /* Names of the months */
27 static const char* months
[] = {
28 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
29 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
32 /* some common prefabricated ASN.1 constants */
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 };
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
);
41 /* some popular algorithmIdentifiers */
43 static u_char ASN1_md5_id_str
[] = {
45 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05,
49 static u_char ASN1_sha1_id_str
[] = {
51 0x06, 0x05, 0x2B, 0x0E,0x03, 0x02, 0x1A,
55 static u_char ASN1_md5WithRSA_id_str
[] = {
57 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04,
61 static u_char ASN1_sha1WithRSA_id_str
[] = {
63 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05,
67 static u_char ASN1_rsaEncryption_id_str
[] = {
69 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
73 const chunk_t ASN1_md5_id
= chunk_from_buf(ASN1_md5_id_str
);
74 const chunk_t ASN1_sha1_id
= chunk_from_buf(ASN1_sha1_id_str
);
75 const chunk_t ASN1_rsaEncryption_id
= chunk_from_buf(ASN1_rsaEncryption_id_str
);
76 const chunk_t ASN1_md5WithRSA_id
= chunk_from_buf(ASN1_md5WithRSA_id_str
);
77 const chunk_t ASN1_sha1WithRSA_id
= chunk_from_buf(ASN1_sha1WithRSA_id_str
);
79 /* ASN.1 definiton of an algorithmIdentifier */
80 static const asn1Object_t algorithmIdentifierObjects
[] = {
81 { 0, "algorithmIdentifier", ASN1_SEQUENCE
, ASN1_NONE
}, /* 0 */
82 { 1, "algorithm", ASN1_OID
, ASN1_BODY
}, /* 1 */
83 { 1, "parameters", ASN1_EOC
, ASN1_RAW
} /* 2 */
86 #define ALGORITHM_ID_ALG 1
87 #define ALGORITHM_ID_PARAMETERS 2
88 #define ALGORITHM_ID_ROOF 3
91 * return the ASN.1 encoded algorithm identifier
93 chunk_t
asn1_algorithmIdentifier(int oid
)
97 case OID_RSA_ENCRYPTION
:
98 return ASN1_rsaEncryption_id
;
99 case OID_MD5_WITH_RSA
:
100 return ASN1_md5WithRSA_id
;
101 case OID_SHA1_WITH_RSA
:
102 return ASN1_sha1WithRSA_id
;
108 return CHUNK_INITIALIZER
;
113 * If the oid is listed in the oid_names table then the corresponding
114 * position in the oid_names table is returned otherwise -1 is returned
116 int known_oid(chunk_t object
)
122 if (oid_names
[oid
].octet
== *object
.ptr
)
124 if (--object
.len
== 0 || oid_names
[oid
].down
== 0)
126 return oid
; /* found terminal symbol */
130 object
.ptr
++; oid
++; /* advance to next hex octet */
135 if (oid_names
[oid
].next
)
136 oid
= oid_names
[oid
].next
;
145 * Decodes the length in bytes of an ASN.1 object
147 u_int
asn1_length(chunk_t
*blob
)
152 /* advance from tag field on to length field */
156 /* read first octet of length field */
161 {/* single length octet */
165 /* composite length, determine number of length octets */
170 logger
->log(logger
, ERROR
|LEVEL1
, "number of length octets is larger than ASN.1 object");
171 return ASN1_INVALID_LENGTH
;
176 logger
->log(logger
, ERROR
|LEVEL1
, "number of length octets is larger than limit of %d octets",
178 return ASN1_INVALID_LENGTH
;
185 len
= 256*len
+ *blob
->ptr
++;
192 * determines if a character string is of type ASN.1 printableString
194 bool is_printablestring(chunk_t str
)
196 const char printablestring_charset
[] =
197 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
200 for (i
= 0; i
< str
.len
; i
++)
202 if (strchr(printablestring_charset
, str
.ptr
[i
]) == NULL
)
209 * Display a date either in local or UTC time
210 * TODO: Does not seem to be thread save
212 char* timetoa(const time_t *time
, bool utc
)
217 sprintf(buf
, "--- -- --:--:--%s----", (utc
)?" UTC ":" ");
220 struct tm
*t
= (utc
)? gmtime(time
) : localtime(time
);
221 sprintf(buf
, "%s %02d %02d:%02d:%02d%s%04d",
222 months
[t
->tm_mon
], t
->tm_mday
, t
->tm_hour
, t
->tm_min
, t
->tm_sec
,
223 (utc
)?" UTC ":" ", t
->tm_year
+ 1900);
229 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
231 time_t asn1totime(const chunk_t
*utctime
, asn1_t type
)
237 if ((eot
= memchr(utctime
->ptr
, 'Z', utctime
->len
)) != NULL
)
239 tz_offset
= 0; /* Zulu time with a zero time zone offset */
241 else if ((eot
= memchr(utctime
->ptr
, '+', utctime
->len
)) != NULL
)
245 sscanf(eot
+1, "%2d%2d", &tz_hour
, &tz_min
);
246 tz_offset
= 3600*tz_hour
+ 60*tz_min
; /* positive time zone offset */
248 else if ((eot
= memchr(utctime
->ptr
, '-', utctime
->len
)) != NULL
)
252 sscanf(eot
+1, "%2d%2d", &tz_hour
, &tz_min
);
253 tz_offset
= -3600*tz_hour
- 60*tz_min
; /* negative time zone offset */
257 return 0; /* error in time format */
261 const char* format
= (type
== ASN1_UTCTIME
)? "%2d%2d%2d%2d%2d":
264 sscanf(utctime
->ptr
, format
, &t
.tm_year
, &t
.tm_mon
, &t
.tm_mday
,
265 &t
.tm_hour
, &t
.tm_min
);
268 /* is there a seconds field? */
269 if ((eot
- utctime
->ptr
) == ((type
== ASN1_UTCTIME
)?12:14))
271 sscanf(eot
-2, "%2d", &t
.tm_sec
);
278 /* representation of year */
279 if (t
.tm_year
>= 1900)
283 else if (t
.tm_year
>= 100)
287 else if (t
.tm_year
< 50)
292 /* representation of month 0..11*/
295 /* set daylight saving time to off */
298 /* compensate timezone */
300 return mktime(&t
) - timezone
- tz_offset
;
304 * Initializes the internal context of the ASN.1 parser
306 void asn1_init(asn1_ctx_t
*ctx
, chunk_t blob
, u_int level0
, bool implicit
)
308 logger
= logger_manager
->get_logger(logger_manager
, ASN1
);
309 ctx
->blobs
[0] = blob
;
310 ctx
->level0
= level0
;
311 ctx
->implicit
= implicit
;
312 memset(ctx
->loopAddr
, '\0', sizeof(ctx
->loopAddr
));
316 * print the value of an ASN.1 simple object
318 static void debug_asn1_simple_object(chunk_t object
, asn1_t type
)
326 oid
= known_oid(object
);
327 if (oid
!= OID_UNKNOWN
)
329 logger
->log(logger
, CONTROL
|LEVEL1
, " '%s'", oid_names
[oid
].name
);
333 case ASN1_UTF8STRING
:
335 case ASN1_PRINTABLESTRING
:
337 case ASN1_VISIBLESTRING
:
338 logger
->log(logger
, CONTROL
|LEVEL1
, " '%.*s'", (int)object
.len
, object
.ptr
);
341 case ASN1_GENERALIZEDTIME
:
342 time
= asn1totime(&object
, type
);
343 logger
->log(logger
, CONTROL
|LEVEL1
, " '%s'", timetoa(&time
, TRUE
));
348 logger
->log_chunk(logger
, RAW
|LEVEL1
, "", object
);
352 * Parses and extracts the next ASN.1 object
354 bool extract_object(asn1Object_t
const *objects
, u_int
*objectID
, chunk_t
*object
, u_int
*level
, asn1_ctx_t
*ctx
)
356 asn1Object_t obj
= objects
[*objectID
];
361 *object
= CHUNK_INITIALIZER
;
363 if (obj
.flags
& ASN1_END
) /* end of loop or option found */
365 if (ctx
->loopAddr
[obj
.level
] && ctx
->blobs
[obj
.level
+1].len
> 0)
367 *objectID
= ctx
->loopAddr
[obj
.level
]; /* another iteration */
368 obj
= objects
[*objectID
];
372 ctx
->loopAddr
[obj
.level
] = 0; /* exit loop or option*/
377 *level
= ctx
->level0
+ obj
.level
;
378 blob
= ctx
->blobs
+ obj
.level
;
380 start_ptr
= blob
->ptr
;
382 /* handle ASN.1 defaults values */
383 if ((obj
.flags
& ASN1_DEF
) && (blob
->len
== 0 || *start_ptr
!= obj
.type
) )
385 /* field is missing */
386 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", *level
, obj
.name
);
387 if (obj
.type
& ASN1_CONSTRUCTED
)
389 (*objectID
)++ ; /* skip context-specific tag */
394 /* handle ASN.1 options */
396 if ((obj
.flags
& ASN1_OPT
)
397 && (blob
->len
== 0 || *start_ptr
!= obj
.type
))
399 /* advance to end of missing option field */
402 while (!((objects
[*objectID
].flags
& ASN1_END
)
403 && (objects
[*objectID
].level
== obj
.level
)));
407 /* an ASN.1 object must possess at least a tag and length field */
411 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN.1 object smaller than 2 octets",
416 blob1
->len
= asn1_length(blob
);
418 if (blob1
->len
== ASN1_INVALID_LENGTH
|| blob
->len
< blob1
->len
)
420 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: length of ASN.1 object invalid or too large",
425 blob1
->ptr
= blob
->ptr
;
426 blob
->ptr
+= blob1
->len
;
427 blob
->len
-= blob1
->len
;
429 /* return raw ASN.1 object without prior type checking */
431 if (obj
.flags
& ASN1_RAW
)
433 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", *level
, obj
.name
);
434 object
->ptr
= start_ptr
;
435 object
->len
= (size_t)(blob
->ptr
- start_ptr
);
439 if (*start_ptr
!= obj
.type
&& !(ctx
->implicit
&& *objectID
== 0))
441 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
442 *level
, obj
.name
, obj
.type
, *start_ptr
);
443 logger
->log_bytes(logger
, RAW
|LEVEL1
, "", start_ptr
, (u_int
)(blob
->ptr
- start_ptr
));
447 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", ctx
->level0
+obj
.level
, obj
.name
);
449 /* In case of "SEQUENCE OF" or "SET OF" start a loop */
450 if (obj
.flags
& ASN1_LOOP
)
454 /* at least one item, start the loop */
455 ctx
->loopAddr
[obj
.level
] = *objectID
+ 1;
459 /* no items, advance directly to end of loop */
462 while (!((objects
[*objectID
].flags
& ASN1_END
)
463 && (objects
[*objectID
].level
== obj
.level
)));
468 if (obj
.flags
& ASN1_OBJ
)
470 object
->ptr
= start_ptr
;
471 object
->len
= (size_t)(blob
->ptr
- start_ptr
);
472 logger
->log_chunk(logger
, RAW
|LEVEL1
, "", *object
);
474 else if (obj
.flags
& ASN1_BODY
)
477 debug_asn1_simple_object(*object
, obj
.type
);
483 * parse an ASN.1 simple type
485 bool parse_asn1_simple_object(chunk_t
*object
, asn1_t type
, u_int level
, const char* name
)
489 /* an ASN.1 object must possess at least a tag and length field */
492 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN.1 object smaller than 2 octets",
497 if (*object
->ptr
!= type
)
499 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
500 level
, name
, type
, *object
->ptr
);
504 len
= asn1_length(object
);
506 if (len
== ASN1_INVALID_LENGTH
|| object
->len
< len
)
508 logger
->log(logger
, ERROR
|LEVEL1
, "L%d - %s: length of ASN.1 object invalid or too large",
513 logger
->log(logger
, CONTROL
|LEVEL1
, "L%d - %s:", level
, name
);
514 debug_asn1_simple_object(*object
, type
);
519 * extracts an algorithmIdentifier
521 int parse_algorithmIdentifier(chunk_t blob
, int level0
, chunk_t
*parameters
)
526 int alg
= OID_UNKNOWN
;
529 asn1_init(&ctx
, blob
, level0
, FALSE
);
531 while (objectID
< ALGORITHM_ID_ROOF
)
533 if (!extract_object(algorithmIdentifierObjects
, &objectID
, &object
, &level
, &ctx
))
538 case ALGORITHM_ID_ALG
:
539 alg
= known_oid(object
);
541 case ALGORITHM_ID_PARAMETERS
:
542 if (parameters
!= NULL
)
543 *parameters
= object
;
554 * tests if a blob contains a valid ASN.1 set or sequence
556 bool is_asn1(chunk_t blob
)
559 u_char tag
= *blob
.ptr
;
561 if (tag
!= ASN1_SEQUENCE
&& tag
!= ASN1_SET
)
563 logger
->log(logger
, ERROR
|LEVEL2
, " file content is not binary ASN.1");
566 len
= asn1_length(&blob
);
569 logger
->log(logger
, ERROR
|LEVEL2
, " file size does not match ASN.1 coded length");
576 * codes ASN.1 lengths up to a size of 16'777'215 bytes
578 void code_asn1_length(size_t length
, chunk_t
*code
)
582 code
->ptr
[0] = length
;
585 else if (length
< 256)
588 code
->ptr
[1] = (u_char
) length
;
591 else if (length
< 65536)
594 code
->ptr
[1] = length
>> 8;
595 code
->ptr
[2] = length
& 0x00ff;
601 code
->ptr
[1] = length
>> 16;
602 code
->ptr
[2] = (length
>> 8) & 0x00ff;
603 code
->ptr
[3] = length
& 0x0000ff;
609 * build an empty asn.1 object with tag and length fields already filled in
611 u_char
* build_asn1_object(chunk_t
*object
, asn1_t type
, size_t datalen
)
613 u_char length_buf
[4];
614 chunk_t length
= { length_buf
, 0 };
617 /* code the asn.1 length field */
618 code_asn1_length(datalen
, &length
);
620 /* allocate memory for the asn.1 TLV object */
621 object
->len
= 1 + length
.len
+ datalen
;
622 object
->ptr
= malloc(object
->len
);
624 /* set position pointer at the start of the object */
627 /* copy the asn.1 tag field and advance the pointer */
630 /* copy the asn.1 length field and advance the pointer */
631 memcpy(pos
, length
.ptr
, length
.len
);
638 * build a simple ASN.1 object
640 chunk_t
asn1_simple_object(asn1_t tag
, chunk_t content
)
644 u_char
*pos
= build_asn1_object(&object
, tag
, content
.len
);
645 memcpy(pos
, content
.ptr
, content
.len
);
651 /* Build an ASN.1 object from a variable number of individual chunks.
652 * Depending on the mode, chunks either are moved ('m') or copied ('c').
654 chunk_t
asn1_wrap(asn1_t type
, const char *mode
, ...)
660 int count
= strlen(mode
);
662 /* sum up lengths of individual chunks */
663 va_start(chunks
, mode
);
665 for (i
= 0; i
< count
; i
++)
667 chunk_t ch
= va_arg(chunks
, chunk_t
);
668 construct
.len
+= ch
.len
;
672 /* allocate needed memory for construct */
673 pos
= build_asn1_object(&construct
, type
, construct
.len
);
675 /* copy or move the chunks */
676 va_start(chunks
, mode
);
677 for (i
= 0; i
< count
; i
++)
679 chunk_t ch
= va_arg(chunks
, chunk_t
);
684 memcpy(pos
, ch
.ptr
, ch
.len
);
690 memcpy(pos
, ch
.ptr
, ch
.len
);
700 * convert a MP integer into a DER coded ASN.1 object
702 chunk_t
asn1_integer_from_mpz(const mpz_t value
)
704 size_t bits
= mpz_sizeinbase(value
, 2); /* size in bits */
706 n
.len
= 1 + bits
/ 8; /* size in bytes */
707 n
.ptr
= mpz_export(NULL
, NULL
, 1, n
.len
, 1, 0, value
);
709 return asn1_wrap(ASN1_INTEGER
, "m", n
);
713 * convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
715 chunk_t
timetoasn1(const time_t *time
, asn1_t type
)
719 char buf
[TIMETOA_BUF
];
720 chunk_t formatted_time
;
721 struct tm
*t
= gmtime(time
);
723 if (type
== ASN1_GENERALIZEDTIME
)
725 format
= "%04d%02d%02d%02d%02d%02dZ";
728 else /* ASN1_UTCTIME */
730 format
= "%02d%02d%02d%02d%02d%02dZ";
731 offset
= (t
->tm_year
< 100)? 0 : -100;
733 sprintf(buf
, format
, t
->tm_year
+ offset
, t
->tm_mon
+ 1, t
->tm_mday
734 , t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
735 formatted_time
.ptr
= buf
;
736 formatted_time
.len
= strlen(buf
);
737 return asn1_simple_object(type
, formatted_time
);