diff options
author | Martin Willi <martin@strongswan.org> | 2006-04-10 14:19:10 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2006-04-10 14:19:10 +0000 |
commit | b5cb0210f7b4672cd1c9e51a5ae6943fcc25331f (patch) | |
tree | 8a18498b075917f81127416cd57659444d440476 /Source/lib/asn1-pluto/asn1-pluto.c | |
parent | 5113680f95e522c677cdd37072cfffbdca06831e (diff) | |
download | strongswan-b5cb0210f7b4672cd1c9e51a5ae6943fcc25331f.tar.bz2 strongswan-b5cb0210f7b4672cd1c9e51a5ae6943fcc25331f.tar.xz |
- startet importing pluto ASN1 stuff
- der PKCS#1 key loading works (as it did with der_decoder)
Diffstat (limited to 'Source/lib/asn1-pluto/asn1-pluto.c')
-rw-r--r-- | Source/lib/asn1-pluto/asn1-pluto.c | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/Source/lib/asn1-pluto/asn1-pluto.c b/Source/lib/asn1-pluto/asn1-pluto.c new file mode 100644 index 000000000..46764e10e --- /dev/null +++ b/Source/lib/asn1-pluto/asn1-pluto.c @@ -0,0 +1,574 @@ +/* Simple ASN.1 parser + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * Copyright (C) 2006 Martin Will, Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "asn1-pluto.h" +#include "oid.h" + +#include <utils/logger_manager.h> + +static logger_t *logger; + +/* Names of the months */ +static const char* months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +/* some common prefabricated ASN.1 constants */ +static u_char ASN1_INTEGER_0_str[] = { 0x02, 0x00 }; +static u_char ASN1_INTEGER_1_str[] = { 0x02, 0x01, 0x01 }; +static u_char ASN1_INTEGER_2_str[] = { 0x02, 0x01, 0x02 }; + +const chunk_t ASN1_INTEGER_0 = chunk_from_buf(ASN1_INTEGER_0_str); +const chunk_t ASN1_INTEGER_1 = chunk_from_buf(ASN1_INTEGER_1_str); +const chunk_t ASN1_INTEGER_2 = chunk_from_buf(ASN1_INTEGER_2_str); + +/* some popular algorithmIdentifiers */ + +static u_char ASN1_md5_id_str[] = { + 0x30, 0x0C, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, + 0x05, 0x00 +}; + +static u_char ASN1_sha1_id_str[] = { + 0x30, 0x09, + 0x06, 0x05, 0x2B, 0x0E,0x03, 0x02, 0x1A, + 0x05, 0x00 +}; + +static u_char ASN1_md5WithRSA_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, + 0x05, 0x00 +}; + +static u_char ASN1_sha1WithRSA_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, + 0x05, 0x00 +}; + +static u_char ASN1_rsaEncryption_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00 +}; + +const chunk_t ASN1_md5_id = chunk_from_buf(ASN1_md5_id_str); +const chunk_t ASN1_sha1_id = chunk_from_buf(ASN1_sha1_id_str); +const chunk_t ASN1_rsaEncryption_id = chunk_from_buf(ASN1_rsaEncryption_id_str); +const chunk_t ASN1_md5WithRSA_id = chunk_from_buf(ASN1_md5WithRSA_id_str); +const chunk_t ASN1_sha1WithRSA_id = chunk_from_buf(ASN1_sha1WithRSA_id_str); + +/* ASN.1 definiton of an algorithmIdentifier */ +static const asn1Object_t algorithmIdentifierObjects[] = { + { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "parameters", ASN1_EOC, ASN1_RAW } /* 2 */ +}; + +#define ALGORITHM_ID_ALG 1 +#define ALGORITHM_ID_PARAMETERS 2 +#define ALGORITHM_ID_ROOF 3 + +/* + * return the ASN.1 encoded algorithm identifier + */ +chunk_t asn1_algorithmIdentifier(int oid) +{ + switch (oid) + { + case OID_RSA_ENCRYPTION: + return ASN1_rsaEncryption_id; + case OID_MD5_WITH_RSA: + return ASN1_md5WithRSA_id; + case OID_SHA1_WITH_RSA: + return ASN1_sha1WithRSA_id; + case OID_MD5: + return ASN1_md5_id; + case OID_SHA1: + return ASN1_sha1_id; + default: + return CHUNK_INITIALIZER; + } +} + +/* + * If the oid is listed in the oid_names table then the corresponding + * position in the oid_names table is returned otherwise -1 is returned + */ +int known_oid(chunk_t object) +{ + int oid = 0; + + while (object.len) + { + if (oid_names[oid].octet == *object.ptr) + { + if (--object.len == 0 || oid_names[oid].down == 0) + { + return oid; /* found terminal symbol */ + } + else + { + object.ptr++; oid++; /* advance to next hex octet */ + } + } + else + { + if (oid_names[oid].next) + oid = oid_names[oid].next; + else + return OID_UNKNOWN; + } + } + return -1; +} + +/* + * Decodes the length in bytes of an ASN.1 object + */ +u_int asn1_length(chunk_t *blob) +{ + u_char n; + size_t len; + + /* advance from tag field on to length field */ + blob->ptr++; + blob->len--; + + /* read first octet of length field */ + n = *blob->ptr++; + blob->len--; + + if ((n & 0x80) == 0) + {/* single length octet */ + return n; + } + + /* composite length, determine number of length octets */ + n &= 0x7f; + + if (n > blob->len) + { + logger->log(logger, ERROR|LEVEL1, "number of length octets is larger than ASN.1 object"); + return ASN1_INVALID_LENGTH; + } + + if (n > sizeof(len)) + { + logger->log(logger, ERROR|LEVEL1, "number of length octets is larger than limit of %d octets", + (int)sizeof(len)); + return ASN1_INVALID_LENGTH; + } + + len = 0; + + while (n-- > 0) + { + len = 256*len + *blob->ptr++; + blob->len--; + } + return len; +} + +/* + * determines if a character string is of type ASN.1 printableString + */ +bool is_printablestring(chunk_t str) +{ + const char printablestring_charset[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?"; + u_int i; + + for (i = 0; i < str.len; i++) + { + if (strchr(printablestring_charset, str.ptr[i]) == NULL) + return FALSE; + } + return TRUE; +} + +/* + * Display a date either in local or UTC time + * TODO: Does not seem to be thread save + */ +char* timetoa(const time_t *time, bool utc) +{ + static char buf[30]; + + if (*time == 0) + sprintf(buf, "--- -- --:--:--%s----", (utc)?" UTC ":" "); + else + { + struct tm *t = (utc)? gmtime(time) : localtime(time); + sprintf(buf, "%s %02d %02d:%02d:%02d%s%04d", + months[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, + (utc)?" UTC ":" ", t->tm_year + 1900); + } + return buf; +} + +/* + * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time + */ +time_t asn1totime(const chunk_t *utctime, asn1_t type) +{ + struct tm t; + time_t tz_offset; + u_char *eot = NULL; + + if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL) + { + tz_offset = 0; /* Zulu time with a zero time zone offset */ + } + else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL) + { + int tz_hour, tz_min; + + sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min); + tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */ + } + else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL) + { + int tz_hour, tz_min; + + sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min); + tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */ + } + else + { + return 0; /* error in time format */ + } + + { + const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d": + "%4d%2d%2d%2d%2d"; + + sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday, + &t.tm_hour, &t.tm_min); + } + + /* is there a seconds field? */ + if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14)) + { + sscanf(eot-2, "%2d", &t.tm_sec); + } + else + { + t.tm_sec = 0; + } + + /* representation of year */ + if (t.tm_year >= 1900) + { + t.tm_year -= 1900; + } + else if (t.tm_year >= 100) + { + return 0; + } + else if (t.tm_year < 50) + { + t.tm_year += 100; + } + + /* representation of month 0..11*/ + t.tm_mon--; + + /* set daylight saving time to off */ + t.tm_isdst = 0; + + /* compensate timezone */ + + return mktime(&t) - timezone - tz_offset; +} + +/* + * Initializes the internal context of the ASN.1 parser + */ +void asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0, bool implicit) +{ + logger = logger_manager->get_logger(logger_manager, ASN1); + ctx->blobs[0] = blob; + ctx->level0 = level0; + ctx->implicit = implicit; + memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr)); +} + +/* + * print the value of an ASN.1 simple object + */ +static void debug_asn1_simple_object(chunk_t object, asn1_t type) +{ + int oid; + time_t time; + + switch (type) + { + case ASN1_OID: + oid = known_oid(object); + if (oid != OID_UNKNOWN) + { + logger->log(logger, CONTROL|LEVEL1, " '%s'", oid_names[oid].name); + return; + } + break; + case ASN1_UTF8STRING: + case ASN1_IA5STRING: + case ASN1_PRINTABLESTRING: + case ASN1_T61STRING: + case ASN1_VISIBLESTRING: + logger->log(logger, CONTROL|LEVEL1, " '%.*s'", (int)object.len, object.ptr); + return; + case ASN1_UTCTIME: + case ASN1_GENERALIZEDTIME: + time = asn1totime(&object, type); + logger->log(logger, CONTROL|LEVEL1, " '%s'", timetoa(&time, TRUE)); + return; + default: + break; + } + logger->log_chunk(logger, RAW|LEVEL1, "", object); +} + +/* + * Parses and extracts the next ASN.1 object + */ +bool extract_object(asn1Object_t const *objects, u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx) +{ + asn1Object_t obj = objects[*objectID]; + chunk_t *blob; + chunk_t *blob1; + u_char *start_ptr; + + *object = CHUNK_INITIALIZER; + + if (obj.flags & ASN1_END) /* end of loop or option found */ + { + if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0) + { + *objectID = ctx->loopAddr[obj.level]; /* another iteration */ + obj = objects[*objectID]; + } + else + { + ctx->loopAddr[obj.level] = 0; /* exit loop or option*/ + return TRUE; + } + } + + *level = ctx->level0 + obj.level; + blob = ctx->blobs + obj.level; + blob1 = blob + 1; + start_ptr = blob->ptr; + + /* handle ASN.1 defaults values */ + if ((obj.flags & ASN1_DEF) && (blob->len == 0 || *start_ptr != obj.type) ) + { + /* field is missing */ + logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", *level, obj.name); + if (obj.type & ASN1_CONSTRUCTED) + { + (*objectID)++ ; /* skip context-specific tag */ + } + return TRUE; + } + + /* handle ASN.1 options */ + + if ((obj.flags & ASN1_OPT) + && (blob->len == 0 || *start_ptr != obj.type)) + { + /* advance to end of missing option field */ + do + (*objectID)++; + while (!((objects[*objectID].flags & ASN1_END) + && (objects[*objectID].level == obj.level))); + return TRUE; + } + + /* an ASN.1 object must possess at least a tag and length field */ + + if (blob->len < 2) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN.1 object smaller than 2 octets", + *level, obj.name); + return FALSE; + } + + blob1->len = asn1_length(blob); + + if (blob1->len == ASN1_INVALID_LENGTH || blob->len < blob1->len) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: length of ASN.1 object invalid or too large", + *level, obj.name); + return FALSE; + } + + blob1->ptr = blob->ptr; + blob->ptr += blob1->len; + blob->len -= blob1->len; + + /* return raw ASN.1 object without prior type checking */ + + if (obj.flags & ASN1_RAW) + { + logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", *level, obj.name); + object->ptr = start_ptr; + object->len = (size_t)(blob->ptr - start_ptr); + return TRUE; + } + + if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0)) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + *level, obj.name, obj.type, *start_ptr); + logger->log_bytes(logger, RAW|LEVEL1, "", start_ptr, (u_int)(blob->ptr - start_ptr)); + return FALSE; + } + + logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", ctx->level0+obj.level, obj.name); + + /* In case of "SEQUENCE OF" or "SET OF" start a loop */ + if (obj.flags & ASN1_LOOP) + { + if (blob1->len > 0) + { + /* at least one item, start the loop */ + ctx->loopAddr[obj.level] = *objectID + 1; + } + else + { + /* no items, advance directly to end of loop */ + do + (*objectID)++; + while (!((objects[*objectID].flags & ASN1_END) + && (objects[*objectID].level == obj.level))); + return TRUE; + } + } + + if (obj.flags & ASN1_OBJ) + { + object->ptr = start_ptr; + object->len = (size_t)(blob->ptr - start_ptr); + logger->log_chunk(logger, RAW|LEVEL1, "", *object); + } + else if (obj.flags & ASN1_BODY) + { + *object = *blob1; + debug_asn1_simple_object(*object, obj.type); + } + return TRUE; +} + +/* + * parse an ASN.1 simple type + */ +bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name) +{ + size_t len; + + /* an ASN.1 object must possess at least a tag and length field */ + if (object->len < 2) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN.1 object smaller than 2 octets", + level, name); + return FALSE; + } + + if (*object->ptr != type) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + level, name, type, *object->ptr); + return FALSE; + } + + len = asn1_length(object); + + if (len == ASN1_INVALID_LENGTH || object->len < len) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: length of ASN.1 object invalid or too large", + level, name); + return FALSE; + } + + logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", level, name); + debug_asn1_simple_object(*object, type); + return TRUE; +} + +/* + * extracts an algorithmIdentifier + */ +int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int alg = OID_UNKNOWN; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE); + + while (objectID < ALGORITHM_ID_ROOF) + { + if (!extract_object(algorithmIdentifierObjects, &objectID, &object, &level, &ctx)) + return OID_UNKNOWN; + + switch (objectID) + { + case ALGORITHM_ID_ALG: + alg = known_oid(object); + break; + case ALGORITHM_ID_PARAMETERS: + if (parameters != NULL) + *parameters = object; + break; + default: + break; + } + objectID++; + } + return alg; + } + +/* + * tests if a blob contains a valid ASN.1 set or sequence + */ +bool is_asn1(chunk_t blob) +{ + u_int len; + u_char tag = *blob.ptr; + + if (tag != ASN1_SEQUENCE && tag != ASN1_SET) + { + logger->log(logger, ERROR|LEVEL2, " file content is not binary ASN.1"); + return FALSE; + } + len = asn1_length(&blob); + if (len != blob.len) + { + logger->log(logger, ERROR|LEVEL2, " file size does not match ASN.1 coded length"); + return FALSE; + } + return TRUE; +} |