2 * Copyright (C) 2000-2002 Andreas Steffen, Zuercher Hochschule Winterthur
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * RCSID $Id: asn1.c,v 0.1 2002/04/12 00:00:00 as Exp $
23 #include "constants.h"
29 /* Some well known object identifiers (OIDs) */
31 const oid_t oid_names[] = {
32 {0x55, 27, 1, "X.500" },
33 { 0x04, 14, 1, "X.509" },
34 { 0x03, 1, 0, "CN" }, /* commonName */
35 { 0x04, 1, 0, "S" }, /* surname */
36 { 0x05, 1, 0, "SN" }, /* serialNumber */
37 { 0x06, 1, 0, "C" }, /* countryMame */
38 { 0x07, 1, 0, "L" }, /* localityName */
39 { 0x08, 1, 0, "ST" }, /* stateOrProvinceName */
40 { 0x0A, 1, 0, "O" }, /* organizationName */
41 { 0x0B, 1, 0, "OU" }, /* organizationalUnitName */
42 { 0x0C, 1, 0, "T" }, /* personalTitle */
43 { 0x0D, 1, 0, "D" }, /* description */
44 { 0x29, 1, 0, "N" }, /* name */
45 { 0x2A, 1, 0, "G" }, /* givenName */
46 { 0x2B, 0, 0, "I" }, /* initials */
47 { 0x1d, 0, 1, "id-ce" },
48 { 0x0E, 1, 0, "subjectKeyIdentifier" },
49 { 0x0F, 1, 0, "keyUsage" },
50 { 0x10, 1, 0, "privateKeyUsagePeriod" },
51 { 0x11, 1, 0, "subjectAltName" }, /* 19 */
52 { 0x12, 1, 0, "issuerAltName" },
53 { 0x13, 1, 0, "basicConstraints" }, /* 21 */
54 { 0x15, 1, 0, "crlReason" },
55 { 0x1F, 1, 0, "crlDistributionPoints" }, /* 23 */
56 { 0x20, 1, 0, "certificatePolicies" },
57 { 0x23, 1, 0, "authorityKeyIdentifier" },
58 { 0x25, 0, 0, "extendedKeyUsage" },
64 { 0x0D, 0, 1, "RSADSI" },
65 { 0x01, 19, 1, "PKCS" },
66 { 0x01, 8, 1, "PKCS-1" },
67 { 0x01, 1, 0, "rsaEncryption" }, /* 35 */
68 { 0x02, 1, 0, "md2WithRSAEncryption" }, /* 36 */
69 { 0x04, 1, 0, "md5WithRSAEncryption" }, /* 37 */
70 { 0x05, 1, 0, "sha-1WithRSAEncryption" }, /* 38 */
71 { 0x0B, 1, 0, "sha256WithRSAEncryption" }, /* 39 */
72 { 0x0C, 1, 0, "sha384WithRSAEncryption" }, /* 40 */
73 { 0x0D, 0, 0, "sha512WithRSAEncryption" }, /* 41 */
74 { 0x07, 7, 1, "PKCS-7" },
75 { 0x01, 1, 0, "data" }, /* 43 */
76 { 0x02, 1, 0, "signedData" }, /* 44 */
77 { 0x03, 1, 0, "envelopedData" },
78 { 0x04, 1, 0, "signedAndEnvelopedData" },
79 { 0x05, 1, 0, "digestedData" },
80 { 0x06, 0, 0, "encryptedData" },
81 { 0x09, 0, 1, "PKCS-9" },
82 { 0x01, 1, 0, "E" }, /* emailAddress 50 */
83 { 0x02, 0, 0, "unstructuredName" },
84 { 0x02, 0, 1, "digestAlgorithm" },
85 { 0x02, 1, 0, "md2" }, /* 53 */
86 { 0x05, 0, 0, "md5" }, /* 54 */
88 { 0x06, 11, 1, "dod" },
89 { 0x01, 0, 1, "internet" },
90 { 0x04, 0, 1, "private" },
91 { 0x01, 0, 1, "enterprise" },
98 { 0x4B, 0, 0, "TCGID" }, /* Trust Center Global ID */
99 { 0x0E, 0, 1, "oiw" },
100 { 0x03, 0, 1, "secsig" },
101 { 0x02, 0, 1, "algorithms" },
102 { 0x1A, 0, 0, "id-SHA-1" }, /* 70 */
106 { 0x01, 0, 1, "organization" },
107 { 0x65, 7, 1, "gov" },
108 { 0x03, 0, 1, "csor" },
109 { 0x04, 0, 1, "nistalgorithm" },
110 { 0x02, 0, 1, "hashalgs" },
111 { 0x01, 1, 0, "id-SHA-256" },
112 { 0x02, 1, 0, "id-SHA-384" },
113 { 0x03, 0, 0, "id-SHA-512" },
118 { 0x01, 1, 0, "nsCertType" },
119 { 0x03, 1, 0, "nsRevocationUrl" },
120 { 0x0d, 0, 0, "nsComment" }
124 /* If the oid is listed in the oid_names table then the corresponding
125 * position in the oid_names table is returned otherwise -1 is returned
128 known_oid(chunk_t object)
134 if (oid_names[oid].digit == *object.ptr)
136 if (--object.len == 0 || oid_names[oid].down == 0)
138 return oid; /* found terminal symbol */
142 object.ptr++; oid++; /* advance to next hex digit */
147 if (oid_names[oid].next)
148 oid += oid_names[oid].next;
157 * Decodes the length in bytes of an ASN.1 object
160 asn1_length(chunk_t *blob)
165 /* advance from tag field on to length field */
169 /* read first octet of length field */
173 if ((n & 0x80) == 0) { /* single length octet */
176 DBG_log("number of length octets is larger than ASN.1 object")
178 return ASN1_INVALID_LENGTH;
183 /* composite length, determine number of length octets */
189 DBG_log("number of length octets is larger than ASN.1 object")
191 return ASN1_INVALID_LENGTH;
197 DBG_log("number of length octets is larger than limit of %d octets"
200 return ASN1_INVALID_LENGTH;
207 len = 256*len + *blob->ptr++;
213 DBG_log("length is larger than remaining blob size")
215 return ASN1_INVALID_LENGTH;
222 * determines if a character string is of type ASN.1 printableString
225 is_printablestring(chunk_t str)
227 const char printablestring_charset[] =
228 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";
231 for (i = 0; i < str.len; i++)
233 if (strchr(printablestring_charset, str.ptr[i]) == NULL)
240 * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
243 asn1totime(const chunk_t *utctime, asn1_t type)
249 if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
251 tz_offset = 0; /* Zulu time with a zero time zone offset */
253 else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
257 if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
259 return 0; /* error in positive timezone offset format */
262 tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */
264 else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
268 if (sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min) != 2)
270 return 0; /* error in negative timezone offset format */
272 tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */
276 return 0; /* error in time format */
280 const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
283 if (sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday,
284 &t.tm_hour, &t.tm_min) != 5)
286 return 0; /* error in time st [yy]yymmddhhmm time format */
290 /* is there a seconds field? */
291 if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
293 if (sscanf(eot-2, "%2d", &t.tm_sec) != 1)
295 return 0; /* error in ss seconds field format */
303 /* representation of year */
304 if (t.tm_year >= 1900)
308 else if (t.tm_year >= 100)
312 else if (t.tm_year < 50)
317 if (t.tm_mon < 1 || t.tm_mon > 12)
319 return 0; /* error in month format */
321 /* representation of month 0..11 in struct tm */
324 /* set daylight saving time to off */
327 /* compensate timezone */
329 return mktime(&t) - tz_offset;
333 * Initializes the internal context of the ASN.1 parser
336 asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0,
337 bool implicit, u_int cond)
339 ctx->blobs[0] = blob;
340 ctx->level0 = level0;
341 ctx->implicit = implicit;
343 memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr));
347 * Parses and extracts the next ASN.1 object
350 extract_object(asn1Object_t const *objects,
351 u_int *objectID, chunk_t *object, asn1_ctx_t *ctx)
353 asn1Object_t obj = objects[*objectID];
358 *object = empty_chunk;
360 if (obj.flags & ASN1_END) /* end of loop or option found */
362 if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0)
364 *objectID = ctx->loopAddr[obj.level]; /* another iteration */
365 obj = objects[*objectID];
369 ctx->loopAddr[obj.level] = 0; /* exit loop or option*/
374 blob = ctx->blobs + obj.level;
376 start_ptr = blob->ptr;
378 /* handle ASN.1 defaults values */
380 if ( (obj.flags & ASN1_DEF) && (*start_ptr != obj.type) )
382 /* field is missing */
385 DBG_log("L%d - %s:", ctx->level0+obj.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 */
403 } while (!((objects[*objectID].flags & ASN1_END) &&
404 (objects[*objectID].level == obj.level )));
408 blob1->len = asn1_length(blob);
410 if (blob1->len == ASN1_INVALID_LENGTH)
413 DBG_log("L%d - %s: length of ASN1 object too large",
414 ctx->level0+obj.level, obj.name);
419 blob1->ptr = blob->ptr;
420 blob->ptr += blob1->len;
421 blob->len -= blob1->len;
423 if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0))
426 DBG_log("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x",
427 ctx->level0+obj.level, obj.name, obj.type, *start_ptr);
428 DBG_dump("", start_ptr, (u_int)(blob->ptr - start_ptr));
434 DBG_log("L%d - %s:", ctx->level0+obj.level, obj.name);
437 /* In case of "SEQUENCE OF" or "SET OF" start a loop */
439 if (obj.flags & ASN1_LOOP) ctx->loopAddr[obj.level] = *objectID + 1;
441 if (obj.flags & ASN1_OBJ)
443 object->ptr = start_ptr;
444 object->len = (u_int)(blob->ptr - start_ptr);
446 DBG_dump_chunk("", *object);
449 else if (obj.flags & ASN1_BODY)
457 oid = known_oid(*object);
461 DBG_log(" '%s'",oid_names[oid].name);
468 case ASN1_PRINTABLESTRING:
469 case ASN1_VISIBLESTRING:
471 DBG_log(" '%.*s'", (int)object->len, object->ptr);
475 case ASN1_GENERALIZEDTIME:
477 time_t time = asn1totime(object, obj.type);
478 DBG_log(" '%s'", timetoa(&time, TRUE));
486 DBG_dump_chunk("", *object);
493 * tests if a blob contains a valid ASN.1 set or sequence
496 is_asn1(chunk_t blob)
499 u_char tag = *blob.ptr;
501 if (tag != ASN1_SEQUENCE && tag != ASN1_SET)
504 DBG_log(" file content is not binary ASN.1");
508 len = asn1_length(&blob);
512 DBG_log(" file size does not match ASN.1 coded length");
519 /* load an ASN.1 coded file with autodetection
520 * of binary DER and base64 PEM formats
523 load_asn1_file(const char* filename, const char* passphrase,
524 const char* type, chunk_t *blob)
529 fd = fopen(filename, "r");
533 fseek(fd, 0, SEEK_END );
534 blob->len = ftell(fd);
537 log(" %s file '%s' is zero length", type, filename);
542 blob->ptr = alloc_bytes(blob->len, type);
543 bytes = fread(blob->ptr, 1, blob->len, fd);
545 // log(" loaded %s file '%s' (%d bytes)", type, filename, bytes);
552 DBG_log(" file coded in DER format");
558 ugh = pemtobin(blob, passphrase);
565 DBG_log(" file coded in PEM format");
570 ugh = "file coded in unknown format, discarded";
573 /* a conversion error has occured */
574 if (!strcmp(ugh, "file coded in unknown format, discarded"))
581 log(" could not open %s file '%s'", type, filename);