refactoring of the ASN.1 parser
This commit is contained in:
+84
-233
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -20,12 +21,16 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "asn1.h"
|
||||
|
||||
#include <library.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 };
|
||||
@@ -34,7 +39,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,
|
||||
@@ -141,19 +148,8 @@ static const chunk_t ASN1_sha256WithRSA_id = chunk_from_buf(ASN1_sha256WithRSA_i
|
||||
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);
|
||||
|
||||
/* 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
|
||||
/*
|
||||
* Defined in header.
|
||||
*/
|
||||
chunk_t asn1_algorithmIdentifier(int oid)
|
||||
{
|
||||
@@ -190,11 +186,10 @@ chunk_t asn1_algorithmIdentifier(int oid)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -222,8 +217,8 @@ int known_oid(chunk_t object)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the length in bytes of an ASN.1 object
|
||||
/*
|
||||
* Defined in header.
|
||||
*/
|
||||
u_int asn1_length(chunk_t *blob)
|
||||
{
|
||||
@@ -269,27 +264,10 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -364,7 +342,7 @@ time_t asn1totime(const chunk_t *utctime, asn1_t type)
|
||||
/**
|
||||
* 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;
|
||||
@@ -389,31 +367,17 @@ chunk_t timetoasn1(const time_t *time, asn1_t type)
|
||||
return asn1_simple_object(type, formatted_time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the internal context of the ASN.1 parser
|
||||
/*
|
||||
* Defined in header.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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);
|
||||
@@ -430,7 +394,7 @@ 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);
|
||||
}
|
||||
@@ -448,148 +412,10 @@ 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;
|
||||
|
||||
@@ -617,32 +443,42 @@ 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
|
||||
*/
|
||||
int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
|
||||
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
|
||||
|
||||
/*
|
||||
* Defined in header
|
||||
*/
|
||||
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, ALGORITHM_ID_ROOF,
|
||||
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)
|
||||
@@ -651,8 +487,8 @@ int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
objectID++;
|
||||
}
|
||||
parser->destroy(parser);
|
||||
return alg;
|
||||
}
|
||||
|
||||
@@ -688,10 +524,27 @@ bool is_asn1(chunk_t blob)
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
@@ -724,14 +577,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;
|
||||
@@ -751,13 +604,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;
|
||||
|
||||
@@ -770,7 +623,7 @@ chunk_t asn1_simple_object(asn1_t tag, chunk_t content)
|
||||
chunk_t asn1_bitstring(const char *mode, chunk_t content)
|
||||
{
|
||||
chunk_t object;
|
||||
u_char *pos = build_asn1_object(&object, ASN1_BIT_STRING, 1 + content.len);
|
||||
u_char *pos = asn1_build_object(&object, ASN1_BIT_STRING, 1 + content.len);
|
||||
|
||||
*pos++ = 0x00;
|
||||
memcpy(pos, content.ptr, content.len);
|
||||
@@ -804,7 +657,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);
|
||||
@@ -841,26 +694,24 @@ static const asn1Object_t timeObjects[] = {
|
||||
/**
|
||||
* 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, TIME_ROOF, 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;
|
||||
}
|
||||
|
||||
+141
-50
@@ -28,8 +28,6 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <asn1/oid.h>
|
||||
|
||||
|
||||
/**
|
||||
* Definition of some primitive ASN1 types
|
||||
@@ -60,7 +58,6 @@ typedef enum {
|
||||
ASN1_CONSTRUCTED = 0x20,
|
||||
|
||||
ASN1_SEQUENCE = 0x30,
|
||||
|
||||
ASN1_SET = 0x31,
|
||||
|
||||
ASN1_CONTEXT_S_0 = 0x80,
|
||||
@@ -81,61 +78,155 @@ typedef enum {
|
||||
ASN1_CONTEXT_C_5 = 0xA5
|
||||
} asn1_t;
|
||||
|
||||
/* Definition of ASN1 flags */
|
||||
#define ASN1_NONE 0x00
|
||||
#define ASN1_DEF 0x01
|
||||
#define ASN1_OPT 0x02
|
||||
#define ASN1_LOOP 0x04
|
||||
#define ASN1_END 0x08
|
||||
#define ASN1_OBJ 0x10
|
||||
#define ASN1_BODY 0x20
|
||||
#define ASN1_RAW 0x40
|
||||
|
||||
#define ASN1_INVALID_LENGTH 0xffffffff
|
||||
|
||||
/* definition of an ASN.1 object */
|
||||
typedef struct {
|
||||
u_int level;
|
||||
const u_char *name;
|
||||
asn1_t type;
|
||||
u_char flags;
|
||||
} asn1Object_t;
|
||||
|
||||
#define ASN1_MAX_LEVEL 10
|
||||
|
||||
typedef struct {
|
||||
bool implicit;
|
||||
bool private;
|
||||
u_int level0;
|
||||
u_int loopAddr[ASN1_MAX_LEVEL+1];
|
||||
chunk_t blobs[ASN1_MAX_LEVEL+2];
|
||||
} asn1_ctx_t;
|
||||
|
||||
/* some common prefabricated ASN.1 constants */
|
||||
/**
|
||||
* Some common prefabricated ASN.1 constants
|
||||
*/
|
||||
extern const chunk_t ASN1_INTEGER_0;
|
||||
extern const chunk_t ASN1_INTEGER_1;
|
||||
extern const chunk_t ASN1_INTEGER_2;
|
||||
|
||||
/* returns some popular algorithmIdentifiers */
|
||||
extern chunk_t asn1_algorithmIdentifier(int oid);
|
||||
|
||||
extern int known_oid(chunk_t object);
|
||||
extern u_int asn1_length(chunk_t *blob);
|
||||
extern bool is_printablestring(chunk_t str);
|
||||
extern time_t asn1totime(const chunk_t *utctime, asn1_t type);
|
||||
extern chunk_t timetoasn1(const time_t *time, asn1_t type);
|
||||
extern void asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0, bool implicit, bool private);
|
||||
extern bool extract_object(asn1Object_t const *objects, u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx);
|
||||
extern bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name);
|
||||
extern int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters);
|
||||
extern time_t parse_time(chunk_t blob, int level0);
|
||||
/** Some ASN.1 analysis functions */
|
||||
|
||||
extern bool is_asn1(chunk_t blob);
|
||||
/**
|
||||
* Returns some popular algorithmIdentifiers
|
||||
*
|
||||
* @param oid known OID index
|
||||
* @return body of the corresponding OID
|
||||
*/
|
||||
chunk_t asn1_algorithmIdentifier(int oid);
|
||||
|
||||
extern void code_asn1_length(size_t length, chunk_t *code);
|
||||
extern u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen);
|
||||
extern chunk_t asn1_simple_object(asn1_t tag, chunk_t content);
|
||||
extern chunk_t asn1_bitstring(const char *mode, chunk_t content);
|
||||
extern chunk_t asn1_wrap(asn1_t type, const char *mode, ...);
|
||||
/**
|
||||
* Converts an ASN.1 OID into a known OID index
|
||||
*
|
||||
* @param object body of an OID
|
||||
* @return index into the oid_names[] table or OID_UNKNOWN
|
||||
*/
|
||||
int asn1_known_oid(chunk_t object);
|
||||
|
||||
/**
|
||||
* Returns the length of an ASN.1 object
|
||||
* The blob pointer is advanced past the tag length fields
|
||||
*
|
||||
* @param pointer to an ASN.1 coded blob
|
||||
* @return length of ASN.1 object
|
||||
*/
|
||||
u_int asn1_length(chunk_t *blob);
|
||||
|
||||
/**
|
||||
* Parses an ASN.1 algorithmIdentifier object
|
||||
*
|
||||
* @param blob ASN.1 coded blob
|
||||
* @param level0 top-most level offset
|
||||
* @param params returns optional [ASN.1 coded] parameters
|
||||
* @return known OID index or OID_UNKNOWN
|
||||
*/
|
||||
int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *params);
|
||||
|
||||
/**
|
||||
* Parse the top-most level of an ASN.1 object
|
||||
*
|
||||
* @param object ASN.1 coded object
|
||||
* @param type Expected ASN.1 type
|
||||
* @param level0 top-most level offset
|
||||
* @param name descriptive name of object
|
||||
* @return TRUE if parsing successful
|
||||
*/
|
||||
bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level0,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* Print the value of an ASN.1 simple object
|
||||
*
|
||||
* @param object ASN.1 object to be printed
|
||||
* @param type asn1_t type
|
||||
* @param private ASN.1 data is confidential (use debug level 4)
|
||||
*/
|
||||
void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private);
|
||||
|
||||
/**
|
||||
* Converts an ASN.1 UTCTIME or GENERALIZEDTIME string to time_t
|
||||
*
|
||||
* @param utctime body of an ASN.1 coded time object
|
||||
* @param type ASN1_UTCTIME or ASN1_GENERALIZEDTIME
|
||||
* @return time_t in UTC
|
||||
*/
|
||||
time_t asn1_to_time(const chunk_t *utctime, asn1_t type);
|
||||
|
||||
/**
|
||||
* Converts time_t to an ASN.1 UTCTIME or GENERALIZEDTIME string
|
||||
*
|
||||
* @param time time_t in UTC
|
||||
* @param type ASN1_UTCTIME or ASN1_GENERALIZEDTIME
|
||||
* @return body of an ASN.1 code time object
|
||||
*/
|
||||
chunk_t asn1_from_time(const time_t *time, asn1_t type);
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 UTCTIME or GENERALIZEDTIME object
|
||||
*
|
||||
* @param blob ASN.1 coded time object
|
||||
* @param level top-most level offset
|
||||
* @return time_t in UTC
|
||||
*/
|
||||
time_t asn1_parse_time(chunk_t blob, int level0);
|
||||
|
||||
/**
|
||||
* Determines if a binary blob is ASN.1 coded
|
||||
*
|
||||
* @param blob blob to be tested
|
||||
* @return TRUE if blob is ASN.1 coded (SEQUENCE or SET)
|
||||
*/
|
||||
bool is_asn1(chunk_t blob);
|
||||
|
||||
/**
|
||||
* Determines if a character string can be coded as PRINTABLESTRING
|
||||
*
|
||||
* @param str character string to be tested
|
||||
* @return TRUE if no special characters are contained
|
||||
*/
|
||||
bool asn1_is_printablestring(chunk_t str);
|
||||
|
||||
|
||||
/** some ASN.1 synthesis functions */
|
||||
|
||||
/**
|
||||
* Build an empty ASN.1 object with tag and length fields already filled in
|
||||
*
|
||||
* @param object returned object - memory is allocated by function
|
||||
* @param type ASN.1 type to be created
|
||||
* @param datalen size of the body to be created
|
||||
* @return points to the first position in the body
|
||||
*/
|
||||
u_char* asn1_build_object(chunk_t *object, asn1_t type, size_t datalen);
|
||||
|
||||
/**
|
||||
* Build a simple ASN.1 object
|
||||
*
|
||||
* @param tag ASN.1 type to be created
|
||||
* @param content content of the ASN.1 object
|
||||
* @return chunk containing the ASN.1 coded object
|
||||
*/
|
||||
chunk_t asn1_simple_object(asn1_t tag, chunk_t content);
|
||||
|
||||
/**
|
||||
* Build an ASN.1 BITSTRING object
|
||||
*
|
||||
* @param mode 'c' for copy or 'm' for move
|
||||
* @param content content of the BITSTRING
|
||||
* @return chunk containing the ASN.1 coded BITSTRING
|
||||
*/
|
||||
chunk_t asn1_bitstring(const char *mode, chunk_t content);
|
||||
|
||||
/**
|
||||
* Build an ASN.1 object from a variable number of individual chunks
|
||||
*
|
||||
* @param typ ASN.1 type to be created
|
||||
* @param mode for each list member: 'c' for copy or 'm' for move
|
||||
* @return chunk containing the ASN.1 coded object
|
||||
*/
|
||||
chunk_t asn1_wrap(asn1_t type, const char *mode, ...);
|
||||
|
||||
#endif /* ASN1_H_ @}*/
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* $Id: asn1.c 3589 2008-03-13 14:14:44Z martin $
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include "asn1.h"
|
||||
#include "asn1_parser.h"
|
||||
|
||||
#define ASN1_MAX_LEVEL 10
|
||||
|
||||
typedef struct private_asn1_parser_t private_asn1_parser_t;
|
||||
|
||||
/**
|
||||
* Private data of an asn1_cxt_t object.
|
||||
*/
|
||||
struct private_asn1_parser_t {
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
asn1_parser_t public;
|
||||
|
||||
/**
|
||||
* Syntax definition of ASN.1 object
|
||||
*/
|
||||
asn1Object_t const *objects;
|
||||
|
||||
/**
|
||||
* Total number of syntax definition lines
|
||||
*/
|
||||
int roof;
|
||||
|
||||
/**
|
||||
* Current syntax definition line
|
||||
*/
|
||||
int line;
|
||||
|
||||
/**
|
||||
* Current stat of the parsing operation
|
||||
*/
|
||||
bool success;
|
||||
|
||||
/**
|
||||
* Declare object data as private - use debug level 4 to log it
|
||||
*/
|
||||
bool private;
|
||||
|
||||
/**
|
||||
* Top-most type is implicit - ignore it
|
||||
*/
|
||||
bool implicit;
|
||||
|
||||
/**
|
||||
* Top-most parsing level - defaults to 0
|
||||
*/
|
||||
u_int level0;
|
||||
|
||||
/**
|
||||
* Jump back address for loops for each level
|
||||
*/
|
||||
int loopAddr[ASN1_MAX_LEVEL + 1];
|
||||
|
||||
/**
|
||||
* Current parsing pointer for each level
|
||||
*/
|
||||
chunk_t blobs[ASN1_MAX_LEVEL + 2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of asn1_parser_t.iterate
|
||||
*/
|
||||
static bool iterate(private_asn1_parser_t *this, int *objectID, chunk_t *object)
|
||||
{
|
||||
chunk_t *blob, *blob1;
|
||||
u_char *start_ptr;
|
||||
u_int level;
|
||||
asn1Object_t obj;
|
||||
|
||||
*object = chunk_empty;
|
||||
|
||||
/* Terminate if the end of the object syntax definition has been reached */
|
||||
if (++(this->line) >= this->roof)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
obj = this->objects[this->line];
|
||||
|
||||
if (obj.flags & ASN1_END) /* end of loop or option found */
|
||||
{
|
||||
if (this->loopAddr[obj.level] && this->blobs[obj.level+1].len > 0)
|
||||
{
|
||||
this->line = this->loopAddr[obj.level]; /* another iteration */
|
||||
obj = this->objects[this->line];
|
||||
}
|
||||
else
|
||||
{
|
||||
this->loopAddr[obj.level] = 0; /* exit loop or option*/
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
level = this->level0 + obj.level;
|
||||
blob = this->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)
|
||||
{
|
||||
this->line++ ; /* skip context-specific tag */
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* handle ASN.1 options */
|
||||
|
||||
if ((obj.flags & ASN1_OPT)
|
||||
&& (blob->len == 0 || *start_ptr != obj.type))
|
||||
{
|
||||
/* advance to end of missing option field */
|
||||
do
|
||||
{
|
||||
this->line++;
|
||||
}
|
||||
while (!((this->objects[this->line].flags & ASN1_END) &&
|
||||
(this->objects[this->line].level == obj.level)));
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
this->success = FALSE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
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);
|
||||
this->success = 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);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (*start_ptr != obj.type && !(this->implicit && this->line == 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));
|
||||
this->success = FALSE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
DBG2("L%d - %s:", 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 */
|
||||
this->loopAddr[obj.level] = this->line + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no items, advance directly to end of loop */
|
||||
do
|
||||
{
|
||||
this->line++;
|
||||
}
|
||||
while (!((this->objects[this->line].flags & ASN1_END) &&
|
||||
(this->objects[this->line].level == obj.level)));
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.flags & ASN1_OBJ)
|
||||
{
|
||||
object->ptr = start_ptr;
|
||||
object->len = (size_t)(blob->ptr - start_ptr);
|
||||
if (this->private)
|
||||
{
|
||||
DBG4("%B", object);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG3("%B", object);
|
||||
}
|
||||
}
|
||||
else if (obj.flags & ASN1_BODY)
|
||||
{
|
||||
*object = *blob1;
|
||||
asn1_debug_simple_object(*object, obj.type, this->private);
|
||||
}
|
||||
|
||||
end:
|
||||
*objectID = this->line;
|
||||
return this->success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of asn1_parser_t.get_level
|
||||
*/
|
||||
static u_int get_level(private_asn1_parser_t *this)
|
||||
{
|
||||
return this->level0 + this->objects[this->line].level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of asn1_parser_t.set_top_level
|
||||
*/
|
||||
static void set_top_level(private_asn1_parser_t *this, u_int level0)
|
||||
{
|
||||
this->level0 = level0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of asn1_parser_t.set_flags
|
||||
*/
|
||||
static void set_flags(private_asn1_parser_t *this, bool implicit, bool private)
|
||||
{
|
||||
this->implicit = implicit;
|
||||
this->private = private;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of asn1_parser_t.success
|
||||
*/
|
||||
static bool success(private_asn1_parser_t *this)
|
||||
{
|
||||
return this->success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of asn1_parser_t.destroy
|
||||
*/
|
||||
static void destroy(private_asn1_parser_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined in header.
|
||||
*/
|
||||
asn1_parser_t* asn1_parser_create(asn1Object_t const *objects, int roof, chunk_t blob)
|
||||
{
|
||||
private_asn1_parser_t *this = malloc_thing(private_asn1_parser_t);
|
||||
|
||||
memset(this, '\0', sizeof(private_asn1_parser_t));
|
||||
this->objects = objects;
|
||||
this->blobs[0] = blob;
|
||||
this->line = -1;
|
||||
this->roof = roof;
|
||||
this->success = TRUE;
|
||||
|
||||
this->public.iterate = (bool (*)(asn1_parser_t*, int*, chunk_t*))iterate;
|
||||
this->public.get_level = (u_int (*)(asn1_parser_t*))get_level;
|
||||
this->public.set_top_level = (void (*)(asn1_parser_t*, u_int))set_top_level;
|
||||
this->public.set_flags = (void (*)(asn1_parser_t*, bool, bool))set_flags;
|
||||
this->public.success = (bool (*)(asn1_parser_t*))success;
|
||||
this->public.destroy = (void (*)(asn1_parser_t*))destroy;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* $Id: asn1.h 3776 2008-04-07 10:37:14Z martin $
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup asn1_parser asn1_parser
|
||||
* @{ @ingroup asn1
|
||||
*/
|
||||
|
||||
#ifndef ASN1_PARSER_H_
|
||||
#define ASN1_PARSER_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* Definition of ASN1 flags
|
||||
*/
|
||||
#define ASN1_NONE 0x00
|
||||
#define ASN1_DEF 0x01
|
||||
#define ASN1_OPT 0x02
|
||||
#define ASN1_LOOP 0x04
|
||||
#define ASN1_END 0x08
|
||||
#define ASN1_OBJ 0x10
|
||||
#define ASN1_BODY 0x20
|
||||
#define ASN1_RAW 0x40
|
||||
|
||||
typedef struct asn1Object_t asn1Object_t;
|
||||
|
||||
/**
|
||||
* Syntax definition of an ASN.1 object
|
||||
*/
|
||||
struct asn1Object_t{
|
||||
u_int level;
|
||||
const u_char *name;
|
||||
asn1_t type;
|
||||
u_char flags;
|
||||
};
|
||||
|
||||
typedef struct asn1_parser_t asn1_parser_t;
|
||||
|
||||
/**
|
||||
* Public interface of an ASN.1 parser
|
||||
*/
|
||||
struct asn1_parser_t {
|
||||
|
||||
/**
|
||||
* Parse the next ASN.1 object in the hierarchy and return it
|
||||
*
|
||||
* @param objectID current line in the object syntax definition
|
||||
* @param object current object
|
||||
* @return - FALSE if end of object syntax definition was reached
|
||||
* or a parsing error occurred
|
||||
* - TRUE otherwise
|
||||
*/
|
||||
bool (*iterate)(asn1_parser_t *this, int *objectID, chunk_t *object);
|
||||
|
||||
/**
|
||||
* Get the current parsing level
|
||||
*
|
||||
* @return current level
|
||||
*/
|
||||
u_int (*get_level)(asn1_parser_t *this);
|
||||
|
||||
/**
|
||||
* Set the top-most level
|
||||
*
|
||||
* @param level top-most level
|
||||
*/
|
||||
void (*set_top_level)(asn1_parser_t *this, u_int level0);
|
||||
|
||||
/**
|
||||
* Set implicit and private flags
|
||||
*
|
||||
* @param implicit top-most type of object is implicit
|
||||
* @param private object data is private (use debug level 4)
|
||||
*/
|
||||
void (*set_flags)(asn1_parser_t *this, bool implicit, bool private);
|
||||
|
||||
/**
|
||||
* Show final parsing status
|
||||
*
|
||||
* @return TRUE if parsing was successful, FALSE otherwise
|
||||
*/
|
||||
bool (*success)(asn1_parser_t *this);
|
||||
|
||||
/**
|
||||
* Destroy the ASN.1 parser
|
||||
*/
|
||||
void (*destroy)(asn1_parser_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ASN.1 parser
|
||||
*
|
||||
* @param objects syntax definition of the ASN.1 object to be parsed
|
||||
* @param roof number of syntax definition lines
|
||||
* @param blob ASN.1 coded binary blob
|
||||
* @return ASN.1 context
|
||||
*/
|
||||
asn1_parser_t* asn1_parser_create(asn1Object_t const *objects, int roof, chunk_t blob);
|
||||
|
||||
#endif /* ASN1_PARSER_H_ @}*/
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur
|
||||
* Copyright (C) 2001-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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur
|
||||
* Copyright (C) 2001-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
|
||||
@@ -10,6 +12,8 @@
|
||||
* 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef PEM_H_
|
||||
|
||||
Reference in New Issue
Block a user