compute ASN.1 to UTC time without time functions
[strongswan.git] / src / libstrongswan / asn1 / asn1.c
index 7204616..9657c88 100644 (file)
@@ -1,6 +1,8 @@
-/* Simple ASN.1 parser
- * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur
- * Copyright (C) 2006 Martin Will, Hochschule fuer Technik Rapperswil
+/*
+ * Copyright (C) 2006 Martin Will
+ * Copyright (C) 2000-2008 Andreas Steffen
+ *
+ * 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
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
+#include <pthread.h>
 
-#include "asn1.h"
-
-#include <library.h>
+#include <utils.h>
 #include <debug.h>
 
-/* some common prefabricated ASN.1 constants */
+#include "oid.h"
+#include "asn1.h"
+#include "asn1_parser.h"
+
+/**
+ * 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 };
@@ -31,7 +38,9 @@ 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 */
+/**
+ * some popular algorithmIdentifiers
+ */
 
 static u_char ASN1_md2_id_str[] = {
        0x30, 0x0c,
@@ -75,6 +84,13 @@ static u_char ASN1_sha512_id_str[] = {
                  0x05,0x00
 };
 
+static u_char ASN1_md2WithRSA_id_str[] = {
+       0x30, 0x0D,
+                 0x06, 0x09,
+                               0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02,
+                 0x05, 0x00
+};
+
 static u_char ASN1_md5WithRSA_id_str[] = {
        0x30, 0x0D,
                  0x06, 0x09,
@@ -89,36 +105,50 @@ static u_char ASN1_sha1WithRSA_id_str[] = {
                  0x05, 0x00
 };
 
-static u_char ASN1_rsaEncryption_id_str[] = {
+static u_char ASN1_sha256WithRSA_id_str[] = {
        0x30, 0x0D,
                  0x06, 0x09,
-                               0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+                               0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
                  0x05, 0x00
 };
 
-const chunk_t ASN1_md2_id    = chunk_from_buf(ASN1_md2_id_str);
-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_sha256_id = chunk_from_buf(ASN1_sha256_id_str);
-const chunk_t ASN1_sha384_id = chunk_from_buf(ASN1_sha384_id_str);
-const chunk_t ASN1_sha512_id = chunk_from_buf(ASN1_sha512_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 */
+static u_char ASN1_sha384WithRSA_id_str[] = {
+       0x30, 0x0D,
+                 0x06, 0x09,
+                               0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C,
+                 0x05, 0x00
 };
 
-#define ALGORITHM_ID_ALG               1
-#define ALGORITHM_ID_PARAMETERS        2
-#define ALGORITHM_ID_ROOF              3
+static u_char ASN1_sha512WithRSA_id_str[] = {
+       0x30, 0x0D,
+                 0x06, 0x09,
+                               0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D,
+                 0x05, 0x00
+};
 
-/**
- * return the ASN.1 encoded algorithm identifier
+static u_char ASN1_rsaEncryption_id_str[] = {
+       0x30, 0x0D,
+                 0x06, 0x09,
+                               0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+                 0x05, 0x00
+};
+
+static const chunk_t ASN1_md2_id    = chunk_from_buf(ASN1_md2_id_str);
+static const chunk_t ASN1_md5_id    = chunk_from_buf(ASN1_md5_id_str);
+static const chunk_t ASN1_sha1_id   = chunk_from_buf(ASN1_sha1_id_str);
+static const chunk_t ASN1_sha256_id = chunk_from_buf(ASN1_sha256_id_str);
+static const chunk_t ASN1_sha384_id = chunk_from_buf(ASN1_sha384_id_str);
+static const chunk_t ASN1_sha512_id = chunk_from_buf(ASN1_sha512_id_str);
+static const chunk_t ASN1_rsaEncryption_id = chunk_from_buf(ASN1_rsaEncryption_id_str);
+static const chunk_t ASN1_md2WithRSA_id = chunk_from_buf(ASN1_md2WithRSA_id_str);
+static const chunk_t ASN1_md5WithRSA_id = chunk_from_buf(ASN1_md5WithRSA_id_str);
+static const chunk_t ASN1_sha1WithRSA_id = chunk_from_buf(ASN1_sha1WithRSA_id_str);
+static const chunk_t ASN1_sha256WithRSA_id = chunk_from_buf(ASN1_sha256WithRSA_id_str);
+static const chunk_t ASN1_sha384WithRSA_id = chunk_from_buf(ASN1_sha384WithRSA_id_str);
+static const chunk_t ASN1_sha512WithRSA_id = chunk_from_buf(ASN1_sha512WithRSA_id_str);
+
+/*
+ * Defined in header.
  */
 chunk_t asn1_algorithmIdentifier(int oid)
 {
@@ -126,24 +156,39 @@ chunk_t asn1_algorithmIdentifier(int oid)
        {
                case OID_RSA_ENCRYPTION:
                        return ASN1_rsaEncryption_id;
+               case OID_MD2_WITH_RSA:
+                       return ASN1_md2WithRSA_id;
                case OID_MD5_WITH_RSA:
                        return ASN1_md5WithRSA_id;
                case OID_SHA1_WITH_RSA:
                        return ASN1_sha1WithRSA_id;
+               case OID_SHA256_WITH_RSA:
+                       return ASN1_sha256WithRSA_id;
+               case OID_SHA384_WITH_RSA:
+                       return ASN1_sha384WithRSA_id;
+               case OID_SHA512_WITH_RSA:
+                       return ASN1_sha512WithRSA_id;
+               case OID_MD2:
+                       return ASN1_md2_id;
                case OID_MD5:
                        return ASN1_md5_id;
                case OID_SHA1:
                        return ASN1_sha1_id;
+               case OID_SHA256:
+                       return ASN1_sha256_id;
+               case OID_SHA384:
+                       return ASN1_sha384_id;
+               case OID_SHA512:
+                       return ASN1_sha512_id;
                default:
                        return chunk_empty;
        }
 }
 
-/**
- * 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
+/*
+ * Defined in header.
  */
-int known_oid(chunk_t object)
+int asn1_known_oid(chunk_t object)
 {
        int oid = 0;
        
@@ -163,16 +208,52 @@ int known_oid(chunk_t object)
                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
+/*
+ * Defined in header.
+ */
+chunk_t asn1_build_known_oid(int n)
+{
+       chunk_t oid;
+       int i;
+       
+       if (n < 0 || n >= OID_MAX)
+       {
+               return chunk_empty;
+       }
+       
+       i = oid_names[n].level + 1;
+       oid = chunk_alloc(2 + i);
+       oid.ptr[0] = ASN1_OID;
+       oid.ptr[1] = i;
+       
+       do
+       {
+               if (oid_names[n].level >= i)
+               {
+                       n--;
+                       continue;
+               }
+               oid.ptr[--i + 2] = oid_names[n--].octet;
+       }
+       while (i > 0);
+       
+       return oid;
+}
+
+/*
+ * Defined in header.
  */
 u_int asn1_length(chunk_t *blob)
 {
@@ -218,30 +299,20 @@ u_int asn1_length(chunk_t *blob)
        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;
-}
+#define TIME_MAX       0x7fffffff
+
+static const int days[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+static const int tm_leap_1970 = 477;
 
 /**
  * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
  */
-time_t asn1totime(const chunk_t *utctime, asn1_t type)
+time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
 {
-       struct tm t;
-       time_t tz_offset;
+       int tm_year, tm_mon, tm_day, tm_days, tm_hour, tm_min, tm_sec;
+       int tm_leap_4, tm_leap_100, tm_leap_400, tm_leap;
+       int tz_hour, tz_min, tz_offset;
+       time_t tm_secs;
        u_char *eot = NULL;
        
        if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
@@ -250,15 +321,11 @@ time_t asn1totime(const chunk_t *utctime, asn1_t type)
        }
        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 */
        }
@@ -267,60 +334,77 @@ time_t asn1totime(const chunk_t *utctime, asn1_t type)
                return 0; /* error in time format */
        }
        
+       /* parse ASN.1 time string */
        {
-       const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
-                       "%4d%2d%2d%2d%2d";
+               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);
+               sscanf(utctime->ptr, format, &tm_year, &tm_mon, &tm_day, &tm_hour, &tm_min);
        }
        
        /* is there a seconds field? */
        if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
        {
-               sscanf(eot-2, "%2d", &t.tm_sec);
+               sscanf(eot-2, "%2d", &tm_sec);
        }
        else
        {
-               t.tm_sec = 0;
+               tm_sec = 0;
        }
        
-       /* representation of year */
-       if (t.tm_year >= 1900)
+       /* representation of two-digit years */
+       if (type == ASN1_UTCTIME)
        {
-               t.tm_year -= 1900;
+               tm_year += (tm_year < 50) ? 2000 : 1900;
        }
-       else if (t.tm_year >= 100)
+       
+       /* prevent large 32 bit integer overflows */
+       if (sizeof(time_t) == 8 && tm_year > 1938)
        {
-               return 0;
+               return TIME_MAX;
        }
-       else if (t.tm_year < 50)
+
+       /* representation of months as 0..11*/
+       if (tm_mon > 12)
        {
-               t.tm_year += 100;
+               return 0; /* error in time format */
        }
+       tm_mon--;
        
-       /* 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;
+       /* representation of days as 0..30 */
+       tm_day--;
+
+       /* number of leap years between last year and 1970? */
+       tm_leap_4 = (tm_year - 1) / 4;
+       tm_leap_100 = tm_leap_4 / 25;
+       tm_leap_400 = tm_leap_100 / 4;
+       tm_leap = tm_leap_4 - tm_leap_100 + tm_leap_400 - tm_leap_1970;
+
+       /* if date later then February, is the current year a leap year? */
+       if ((tm_mon > 1 && 4*(tm_leap_4 + 1) == tm_year) &&
+               (100*(tm_leap_100 + 1) != tm_year || 400*(tm_leap_400 + 1) == tm_year))
+       {
+               tm_leap++;
+       }       
+       tm_days = 365 * (tm_year - 1970) + days[tm_mon] + tm_day + tm_leap;
+       tm_secs = 60 * (60 * (24 * tm_days + tm_hour) + tm_min) + tm_sec - tz_offset;
+
+       /* has a 32 bit overflow occurred? */
+       return (tm_secs < 0) ? TIME_MAX : tm_secs;
 }
 
 /**
  *  Convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format
  */
-chunk_t timetoasn1(const time_t *time, asn1_t type)
+chunk_t asn1_from_time(const time_t *time, asn1_t type)
 {
        int offset;
        const char *format;
        char buf[BUF_LEN];
        chunk_t formatted_time;
-       struct tm *t = gmtime(time);
+       struct tm t;
        
+       gmtime_r(time, &t);
        if (type == ASN1_GENERALIZEDTIME)
        {
                format = "%04d%02d%02d%02d%02d%02dZ";
@@ -329,40 +413,26 @@ chunk_t timetoasn1(const time_t *time, asn1_t type)
        else /* ASN1_UTCTIME */
        {
                format = "%02d%02d%02d%02d%02d%02dZ";
-               offset = (t->tm_year < 100)? 0 : -100;
+               offset = (t.tm_year < 100)? 0 : -100;
        }
-       snprintf(buf, BUF_LEN, format, t->tm_year + offset, 
-                        t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+       snprintf(buf, BUF_LEN, format, t.tm_year + offset, 
+                        t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
        formatted_time.ptr = buf;
        formatted_time.len = strlen(buf);
        return asn1_simple_object(type, formatted_time);
 }
 
-
-/**
- * Initializes the internal context of the ASN.1 parser
- */
-void asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0,
-                          bool implicit, bool private)
-{
-       ctx->blobs[0] = blob;
-       ctx->level0   = level0;
-       ctx->implicit = implicit;
-    ctx->private  = private;
-       memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr));
-}
-
-/**
- * print the value of an ASN.1 simple object
+/*
+ * Defined in header.
  */
-static void debug_asn1_simple_object(chunk_t object, asn1_t type, bool private)
+void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private)
 {
        int oid;
        
        switch (type)
        {
                case ASN1_OID:
-                       oid = known_oid(object);
+                       oid = asn1_known_oid(object);
                        if (oid != OID_UNKNOWN)
                        {
                                DBG2("  '%s'", oid_names[oid].name);
@@ -379,9 +449,9 @@ static void debug_asn1_simple_object(chunk_t object, asn1_t type, bool private)
                case ASN1_UTCTIME:
                case ASN1_GENERALIZEDTIME:
                        {
-                               time_t time = asn1totime(&object, type);
+                               time_t time = asn1_to_time(&object, type);
 
-                               DBG2("  '%T'", &time);
+                               DBG2("  '%T'", &time, TRUE);
                        }
                        return;
                default:
@@ -398,147 +468,9 @@ static void debug_asn1_simple_object(chunk_t object, asn1_t type, bool private)
 }
 
 /**
- * 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_empty;
-       
-       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 */
-               DBG2("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)
-       {
-               DBG1("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)
-       {
-               DBG1("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)
-       {
-               DBG2("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))
-       {
-               DBG1("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
-                                       *level, obj.name, obj.type, *start_ptr);
-               DBG3("%b", start_ptr, (u_int)(blob->ptr - start_ptr));
-               return FALSE;
-       }
-       
-       DBG2("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);
-               if (ctx->private)
-               {
-                       DBG4("%B", object);
-               }
-               else
-               {
-                       DBG3("%B", object);
-               }
-       }
-       else if (obj.flags & ASN1_BODY)
-       {
-               *object = *blob1;
-               debug_asn1_simple_object(*object, obj.type, ctx->private);
-       }
-       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)
+bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name)
 {
        size_t len;
        
@@ -566,44 +498,56 @@ bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level, const c
        }
        
        DBG2("L%d - %s:", level, name);
-       debug_asn1_simple_object(*object, type, FALSE);
+       asn1_debug_simple_object(*object, type, FALSE);
        return TRUE;
 }
 
 /**
- * extracts an algorithmIdentifier
+ * ASN.1 definition 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|ASN1_OPT }, /* 2 */
+       { 1,   "end opt",                       ASN1_EOC,               ASN1_END              }, /* 3 */
+       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT         }
+};
+#define ALGORITHM_ID_ALG                       1
+#define ALGORITHM_ID_PARAMETERS                2
+
+/*
+ * Defined in header
  */
-int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
+int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
+       int objectID;
        int alg = OID_UNKNOWN;
-       int objectID = 0;
        
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
+       parser = asn1_parser_create(algorithmIdentifierObjects, blob);
+       parser->set_top_level(parser, level0);
        
-       while (objectID < ALGORITHM_ID_ROOF)
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(algorithmIdentifierObjects, &objectID, &object, &level, &ctx))
-                       return OID_UNKNOWN;
-               
                switch (objectID)
                {
                        case ALGORITHM_ID_ALG:
-                               alg = known_oid(object);
+                               alg = asn1_known_oid(object);
                                break;
                        case ALGORITHM_ID_PARAMETERS:
                                if (parameters != NULL)
+                               {
                                        *parameters = object;
+                               }
                                break;
                        default:
                                break;
                }
-               objectID++;
        }
+       parser->destroy(parser);
        return alg;
- }
+}
 
 /*
  *  tests if a blob contains a valid ASN.1 set or sequence
@@ -618,11 +562,38 @@ bool is_asn1(chunk_t blob)
                DBG2("  file content is not binary ASN.1");
                return FALSE;
        }
+
        len = asn1_length(&blob);
-       if (len != blob.len)
+
+       /* exact match */
+       if (len == blob.len)
        {
-               DBG2("  file size does not match ASN.1 coded length");
-               return FALSE;
+               return TRUE;
+       }
+
+       /* some websites append a surplus newline character to the blob */
+       if (len + 1 == blob.len && *(blob.ptr + len) == '\n')
+       {
+               return TRUE;
+       }
+
+       DBG2("  file size does not match ASN.1 coded length");
+       return FALSE;
+}
+
+/*
+ * Defined in header.
+ */
+bool asn1_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;
 }
@@ -630,7 +601,7 @@ bool is_asn1(chunk_t blob)
 /**
  * codes ASN.1 lengths up to a size of 16'777'215 bytes
  */
-void code_asn1_length(size_t length, chunk_t *code)
+static void asn1_code_length(size_t length, chunk_t *code)
 {
        if (length < 128)
        {
@@ -663,14 +634,14 @@ void code_asn1_length(size_t length, chunk_t *code)
 /**
  * build an empty asn.1 object with tag and length fields already filled in
  */
-u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen)
+u_char* asn1_build_object(chunk_t *object, asn1_t type, size_t datalen)
 {
        u_char length_buf[4];
        chunk_t length = { length_buf, 0 };
        u_char *pos;
        
        /* code the asn.1 length field */
-       code_asn1_length(datalen, &length);
+       asn1_code_length(datalen, &length);
        
        /* allocate memory for the asn.1 TLV object */
        object->len = 1 + length.len + datalen;
@@ -690,13 +661,13 @@ u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen)
 }
 
 /**
- * build a simple ASN.1 object
+ * Build a simple ASN.1 object
  */
 chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
 {
        chunk_t object;
        
-       u_char *pos = build_asn1_object(&object, tag, content.len);
+       u_char *pos = asn1_build_object(&object, tag, content.len);
        memcpy(pos, content.ptr, content.len); 
        pos += content.len;
        
@@ -704,6 +675,23 @@ chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
 }
 
 /**
+ * Build an ASN.1 BITSTRING object
+ */
+chunk_t asn1_bitstring(const char *mode, chunk_t content)
+{
+       chunk_t object;
+       u_char *pos = asn1_build_object(&object, ASN1_BIT_STRING, 1 + content.len);
+
+       *pos++ = 0x00;
+       memcpy(pos, content.ptr, content.len);
+       if (*mode == 'm')
+       {
+               free(content.ptr);
+       }
+       return object;
+}
+
+/**
  * Build an ASN.1 object from a variable number of individual chunks.
  * Depending on the mode, chunks either are moved ('m') or copied ('c').
  */
@@ -726,7 +714,7 @@ chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
        va_end(chunks);
        
        /* allocate needed memory for construct */
-       pos = build_asn1_object(&construct, type, construct.len);
+       pos = asn1_build_object(&construct, type, construct.len);
        
        /* copy or move the chunks */
        va_start(chunks, mode);
@@ -734,17 +722,12 @@ chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
        {
                chunk_t ch = va_arg(chunks, chunk_t);
                
-               switch (*mode++)
+               memcpy(pos, ch.ptr, ch.len);
+               pos += ch.len;
+
+               if (*mode++ == 'm')
                {
-                       case 'm':
-                               memcpy(pos, ch.ptr, ch.len); 
-                               pos += ch.len;
-                               free(ch.ptr);
-                               break;
-                       case 'c':
-                       default:
-                               memcpy(pos, ch.ptr, ch.len); 
-                               pos += ch.len;
+                       free(ch.ptr);
                }
        }
        va_end(chunks);
@@ -753,55 +736,39 @@ chunk_t asn1_wrap(asn1_t type, const char *mode, ...)
 }
 
 /**
- * convert a MP integer into a DER coded ASN.1 object
- */
-chunk_t asn1_integer_from_mpz(const mpz_t value)
-{
-       size_t bits = mpz_sizeinbase(value, 2);  /* size in bits */
-       chunk_t n;
-
-       n.len = 1 + bits / 8;  /* size in bytes */      
-       n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, value);
-
-       return asn1_wrap(ASN1_INTEGER, "m", n);
-}
-
-/**
  * ASN.1 definition of time
  */
 static const asn1Object_t timeObjects[] = {
-       { 0,   "utcTime",               ASN1_UTCTIME,                   ASN1_OPT|ASN1_BODY      }, /*  0 */
-       { 0,   "end opt",               ASN1_EOC,                               ASN1_END                        }, /*  1 */
-       { 0,   "generalizeTime",ASN1_GENERALIZEDTIME,   ASN1_OPT|ASN1_BODY      }, /*  2 */
-       { 0,   "end opt",               ASN1_EOC,                               ASN1_END                        }  /*  3 */
+       { 0, "utcTime",                 ASN1_UTCTIME,                   ASN1_OPT|ASN1_BODY      }, /* 0 */
+       { 0, "end opt",                 ASN1_EOC,                               ASN1_END                        }, /* 1 */
+       { 0, "generalizeTime",  ASN1_GENERALIZEDTIME,   ASN1_OPT|ASN1_BODY      }, /* 2 */
+       { 0, "end opt",                 ASN1_EOC,                               ASN1_END                        }, /* 3 */
+       { 0, "exit",                    ASN1_EOC,                               ASN1_EXIT                       }
 };
 #define TIME_UTC                       0
 #define TIME_GENERALIZED       2
-#define TIME_ROOF                      4
 
 /**
  * extracts and converts a UTCTIME or GENERALIZEDTIME object
  */
-time_t parse_time(chunk_t blob, int level0)
+time_t asn1_parse_time(chunk_t blob, int level0)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
+       int objectID;
+       time_t utc_time = 0;
        
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
+       parser= asn1_parser_create(timeObjects, blob);
+       parser->set_top_level(parser, level0);
        
-       while (objectID < TIME_ROOF)
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(timeObjects, &objectID, &object, &level, &ctx))
-                       return 0;
-               
                if (objectID == TIME_UTC || objectID == TIME_GENERALIZED)
                {
-                       return asn1totime(&object, (objectID == TIME_UTC)
-                                       ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME);
+                       utc_time = asn1_to_time(&object, (objectID == TIME_UTC)
+                                                                       ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME);
                }
-               objectID++;
        }
-       return 0;
+       parser->destroy(parser);
+       return utc_time;
 }