1/*
2 * Decodes and prints ASN.1 BER.
3 *
4 * Written by Henning Schulzrinne, Columbia University, (c) 1997.
5 * Updated by Doug Mitchell 9/6/00 to use oidParser
6 */
7
8#include <stdio.h>
9#include <ctype.h>
10#include <string.h>
11#include <stdlib.h>
12#include <security_cdsa_utils/cuFileIo.h>
13#include <security_cdsa_utils/cuOidParser.h>
14
15static void usage(char **argv)
16{
17	printf("Usage: %s berfile [o (parse octet strings)] "
18		"[b (parse bit strings) ]\n", argv[0]);
19	exit(1);
20}
21
22#define PARSE_BIT_STR		0x01
23#define PARSE_OCTET_STR		0x02
24
25/* bogus infinite length loop index target */
26#define INDEFINITE 1000000
27
28typedef unsigned char uchar;
29
30#define CLASS_MASK		0xc0
31#define CLASS_UNIV		0x00
32#define CLASS_APPL		0x40
33#define CLASS_CONTEXT	0x80
34#define CLASS_PRIVATE	0xc0
35#define CONSTRUCT_BIT	0x20
36
37static int sequence(int t, const char *tag, uchar *b, int length,
38	OidParser &parser, unsigned parseFlags);
39static int value(int t, uchar *b, int length, OidParser &parser,
40	unsigned parseFlags);
41
42static void indent(int t)
43{
44  int i;
45  for (i = 0; i < t; i++) putchar(' ');
46} /* indent */
47
48static int oid(int t, uchar *b, int length, OidParser &parser)
49{
50  indent(t);
51  char oidStr[OID_PARSER_STRING_SIZE];
52  parser.oidParse(b, length, oidStr);
53  printf("OID <%d>: %s\n", length, oidStr);
54  return length;
55} /* oid */
56
57
58static int integer(int t, const char *type, uchar *b, int length)
59{
60  int i;
61
62  indent(t);
63  printf("%s <%d>:", type, length);
64  for (i = 0; i < length; i++) {
65    printf("%02x ", b[i]);
66  }
67  printf("\n");
68  return length;
69} /* integer */
70
71
72static int bitstring(int t, uchar *b, int length)
73{
74  int i;
75
76  indent(t);
77  printf("BIT STRING <%d, %d>:", length, b[0]);
78  for (i = 1; i < length; i++) {
79    if ((i % 16) == 0) {
80      printf("\n");
81      indent(t+3);
82    }
83    printf("%02x ", b[i]);
84  }
85  printf("\n");
86  return length;
87} /* bitstring */
88
89
90static int octetstring(int t, uchar *b, int length)
91{
92  int i;
93
94  indent(t);
95  printf("OCTET STRING <%d>:", length);
96  for (i = 0; i < length; i++) {
97    if ((i % 16) == 0) {
98      printf("\n");
99      indent(t+3);
100    }
101    printf("%02x ", b[i]);
102  }
103  printf("\n");
104  return length;
105} /* bitstring */
106
107static int bmpstring(int t, uchar *b, int length)
108{
109	/* enventually convert via unicode to printable */
110	int i;
111
112	indent(t);
113	printf("BMP STRING <%d>:", length);
114	for (i = 0; i < length; i++) {
115		if ((i % 16) == 0) {
116			printf("\n");
117			indent(t+3);
118		}
119		printf("%02x ", b[i]);
120	}
121	printf("\n");
122	return length;
123} /* bmpstring */
124
125
126
127static int string(int t, const char *tag, uchar *b, int length)
128{
129  indent(t);
130  printf("%s <%d>: %*.*s\n", tag, length, length, length, b);
131  return length;
132} /* string */
133
134static int unparsed(int t, uchar *b, int length)
135{
136  int i;
137
138  indent(t);
139  printf("UNPARSED DATA <%d>:", length);
140  for (i = 0; i < length; i++) {
141    if ((i % 16) == 0) {
142      printf("\n");
143      indent(t+3);
144    }
145    printf("%02x ", b[i]);
146  }
147  printf("\n");
148  return length;
149} /* unparsed */
150
151
152static int metaClass(int t, 	// indent
153	uchar		tag,			// tag
154	uchar 		*b, 			// start of contents
155	int 		length, 		// content length
156	const char 	*className,
157	OidParser 	&parser,
158	unsigned 	parseFlags)
159{
160	uchar underlyingTag = tag & ~(CLASS_MASK | CONSTRUCT_BIT);
161	indent(t);
162	if(length == -1) {
163		printf("%s (tag %d) <indefinite> {\n", className, underlyingTag);
164	}
165	else {
166		printf("%s (tag %d) <%d> {\n", className, underlyingTag, length);
167	}
168	/* just print uninterpreted bytes if !constructed, !universal */
169	if( ( ( tag & CLASS_MASK) != CLASS_UNIV) &&
170		!(tag & CONSTRUCT_BIT) ) {
171		/* fixme - can't do this for indefinite length */
172		unparsed(t+3, b, length);
173	}
174	else {
175		length = value(t + 3, b, length, parser, parseFlags);
176	}
177	indent(t);
178	printf("}\n");
179	return length;
180} /* metaClass */
181
182
183static int value(
184	int t, 					// indent depth
185	uchar *b, 				// start of item (at tag)
186	int length, 			// length if item, -1 ==> indefinite
187	OidParser &parser,
188	unsigned parseFlags)	// 	PARSE_BIT_STR, etc.
189{
190	int i, j, k;
191	int tag_length, length_length, len;
192	uchar classId;
193	uchar constructBit;
194	const char *parseStr = NULL;	// name of recursively parsed bit/octet string
195	uchar *parseVal = NULL;	// contents to parse
196	unsigned parseLen = 0;
197
198	if (length == -1) length = INDEFINITE;
199
200	for (i = 0; i < length; ) {
201		/* tag length */
202		tag_length = 1;
203
204		/* get length: short form (single byte) or 0x8b or 0x80 (indefinite) */
205		if (b[i+1] == 0x80) {
206			len = -1;
207			length_length = 1;
208		}
209		else if (b[i+1] & 0x80) {
210			/* long length of n bytes */
211			length_length = (b[i+1] & 0x7f) + 1;
212			len = 0;
213			for (j = 1; j < length_length; j++) {
214			len = len * 256 + b[i+1+j];
215			}
216		}
217		else {
218			/* short length form */
219			len = b[i+1];
220			length_length = 1;
221		}
222
223		/*
224		 * i is index of current tag
225		 * len is content length of current item
226		 * set k as index to start of content
227		 */
228		k = i + tag_length + length_length;
229
230		if(length != INDEFINITE) {
231			if((k + len) > length) {
232				printf("***content overflow\n");
233			}
234		}
235		/* handle special case classes */
236		classId = b[i] & CLASS_MASK;
237		constructBit = b[i] & CONSTRUCT_BIT;
238
239		switch(classId) {
240			case CLASS_UNIV:		// normal case handled below
241				goto parseTag;
242			case CLASS_CONTEXT:
243				i += metaClass(t, b[i], &b[k], len, "CONTEXT SPECIFIC",
244					parser, parseFlags);
245				break;
246			case CLASS_APPL:
247				i += metaClass(t, b[i], &b[k], len, "APPLICATION SPECIFIC",
248					parser, parseFlags);
249				break;
250			case CLASS_PRIVATE:
251				i += metaClass(t, b[i], &b[k], len, "PRIVATE",
252					parser, parseFlags);
253				break;
254			default:
255				/* not reached */
256				break;
257		}
258		goto done;			// this item already parsed per class
259	parseTag:
260		parseStr = NULL;
261		parseVal = b + k;	// default recursively parsed value
262		parseLen = len;
263
264		switch(b[i]) {
265			case 0x0:    /* end of indefinite length */
266				i += tag_length + length_length;
267				return i;
268
269			case 0x01:	 /* BOOLEAN, a one-byte integer */
270				i += integer(t, "BOOLEAN", &b[k], len);
271				break;
272
273			case 0x02:   /* INTEGER */
274				i += integer(t, "INTEGER", &b[k], len);
275				break;
276
277			case 0x03:   /* BIT STRING */
278				i += bitstring(t, &b[k], len);
279				if(parseFlags & PARSE_OCTET_STR) {
280					parseStr = "BIT STRING";
281					parseVal++;	// skip reminder byte
282					parseLen--;
283				}
284				break;
285
286			case 0x04:   /* OCTET STRING */
287				i += octetstring(t, &b[k], len);
288				if(parseFlags & PARSE_OCTET_STR) {
289					parseStr = "OCTET STRING";
290				}
291				break;
292
293			case 0x5:    /* NULL */
294				indent(t);
295				printf("NULL\n");
296				break;
297
298			case 0x06:   /* OBJECT IDENTIFIER */
299				i += oid(t, &b[k], len, parser);
300				break;
301
302			case 0x0A:  /* enumerated */
303				i += integer(t, "ENUM", &b[k], len);
304				break;
305
306			case 0xc:   /* UTF8 string */
307				i += string(t, "UTF8String", &b[k], len);
308				break;
309
310			case 0x13:   /* PrintableString */
311				i += string(t, "PrintableString", &b[k], len);
312				break;
313
314			case 0x14:   /* T61String */
315				i += string(t, "T61String", &b[k], len);
316				break;
317
318			case 0x16:   /* IA5String */
319				i += string(t, "IA5String", &b[k], len);
320				break;
321
322			case 0x17:   /* UTCTime */
323				i += string(t, "UTCTime", &b[k], len);
324				break;
325
326			case 0x18:   /* generalized Time */
327				i += string(t, "GenTime", &b[k], len);
328				break;
329
330			case 0x19:	/* SEC_ASN1_GRAPHIC_STRING */
331				i += string(t, "Graphic", &b[k], len);
332				break;
333
334			case 0x1a:	/* SEC_ASN1_VISIBLE_STRING */
335				i += string(t, "Visible", &b[k], len);
336				break;
337
338			case 0x1b:	/* SEC_ASN1_GENERAL_STRING */
339				i += string(t, "General", &b[k], len);
340				break;
341
342			case 0x1e:	/* BMP string, unicode */
343				i += bmpstring(t, &b[k], len);
344				break;
345
346			case 0xA0:   /* SIGNED? */
347				i += sequence(t, "EXPLICIT", &b[k], len, parser,
348					parseFlags);
349				break;
350
351			case 0x30:   /* SEQUENCE OF */
352				i += sequence(t, "SEQUENCE OF", &b[k], len, parser,
353					parseFlags);
354				break;
355
356			case 0x31:   /* SET OF */
357				i += sequence(t, "SET OF", &b[k], len, parser,
358					parseFlags);
359				break;
360
361			case 0x39:   /* SET OF */
362				i += sequence(t, "structured", &b[k], len, parser,
363					parseFlags);
364				break;
365
366			case 0x24:	/* CONSTRUCTED octet string */
367				i += sequence(t, "CONSTR OCTET STRING", &b[k], len,
368					parser, parseFlags);
369				if(parseFlags & PARSE_OCTET_STR) {
370					parseStr = "OCTET STRING";
371				}
372				break;
373
374			default:
375				printf("ACK! Unknown tag (0x%x); aborting\n", b[i]);
376				exit(1);
377		}	/* switch tag */
378	done:
379		if(parseStr) {
380			indent(t);
381			fpurge(stdin);
382			printf("Parse contents (y/anything)? ");
383			char resp = getchar();
384			if(resp == 'y') {
385				indent(t+3);
386				printf("Parsed %s contents {\n", parseStr);
387				value(t+6, parseVal, parseLen, parser, parseFlags);
388				indent(t+3);
389				printf("} end of Parsed %s\n", parseStr);
390			}
391		}
392		i += tag_length + length_length;
393	}	/* main loop for i to length */
394	return i;
395} /* value */
396
397
398static int sequence(int t, const char *tag, uchar *b, int length,
399	OidParser &parser, unsigned parseFlags)
400{
401  int len;
402
403  indent(t);
404  if (length < 0) {
405    printf("%s <indefinite> {\n", tag);
406  }
407  else {
408    printf("%s <%d> {\n", tag, length);
409  }
410  len = value(t + 3, b, length, parser, parseFlags);
411  indent(t);
412  printf("}\n");
413  return len;
414} /* sequence */
415
416
417int main(int argc, char *argv[])
418{
419	uchar* bfr;
420	int i = 0;
421	if(argc < 2) {
422		usage(argv);
423	}
424	if(readFile(argv[1], &bfr, (unsigned int *)&i)) {
425		printf("Error reading %s\n", argv[1]);
426		exit(1);
427	}
428
429	unsigned parseFlags = 0;
430
431	for(int dex=2; dex<argc; dex++) {
432		switch(argv[dex][0]) {
433			case 'b':
434				parseFlags |= PARSE_BIT_STR;
435				break;
436			case 'o':
437				parseFlags |= PARSE_OCTET_STR;
438				break;
439			default:
440				usage(argv);
441		}
442	}
443
444	OidParser parser;
445	value(0, bfr, i, parser, parseFlags);
446	free(bfr);
447	return 0;
448} /* main */
449