2 * Copyright (c) 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include "sys-common.h"
30 #define ASN_DISABLE_PER_SUPPORT 1
31 #define ASN_DISABLE_OER_SUPPORT 1
33 #include <asn1parser.h> /* For static string tables */
35 #include <asn_application.h>
36 #include <constraints.c>
37 #include <ber_tlv_tag.c>
38 #include <ber_tlv_length.c>
40 #include <OBJECT_IDENTIFIER.c>
41 #include <RELATIVE-OID.c>
42 #include <asn_codecs_prim.c>
43 #include <asn1p_integer.c>
44 #include <asn_internal.c>
47 #define COPYRIGHT "Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>\n"
49 static void usage(const char *av0); /* Print the Usage screen and exit */
50 static int process(const char *fname); /* Perform the BER decoding */
51 static int decode_tlv_from_string(const char *datastring);
53 static int single_type_decoding = 0; /* -1 enables that */
54 static int minimalistic = 0; /* -m enables that */
55 static int pretty_printing = 1; /* -p disables that */
56 static long skip_bytes = 0; /* -s controls that */
57 static char indent_bytes[16] = " "; /* -i controls that */
60 main(int ac, char **av) {
61 int ch; /* Command line character */
62 int i; /* Index in some loops */
65 * Process command-line options.
67 while((ch = getopt(ac, av, "1hi:mps:t:v")) != -1) switch(ch) {
69 single_type_decoding = 1;
73 if(i >= 0 && i < (int)sizeof(indent_bytes)) {
74 memset(indent_bytes, ' ', i);
75 indent_bytes[i] = '\0';
77 fprintf(stderr, "-i %s: Invalid indent value\n", optarg);
88 skip_bytes = atol(optarg);
90 fprintf(stderr, "-s %s: positive value expected\n", optarg);
95 if(decode_tlv_from_string(optarg)) exit(EX_DATAERR);
98 fprintf(stderr, "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT);
107 * Ensure that there are some input files present.
113 fprintf(stderr, "%s: No input files specified\n", av[0]);
117 setvbuf(stdout, 0, _IOLBF, 0);
120 * Iterate over input files and parse each.
121 * All syntax trees from all files will be bundled together.
123 for(i = 0; i < ac; i++) {
124 if(process(av[i])) exit(EX_DATAERR);
131 * Print the usage screen and exit(EX_USAGE).
134 usage(const char *av0) {
135 /* clang-format off */
137 "ASN.1 BER Decoder, v" VERSION "\n" COPYRIGHT
138 "Usage: %s [options] [-] [file ...]\n"
140 " -1 Decode only the first BER structure (otherwise, until EOF)\n"
141 " -i <indent> Amount of spaces for output indentation (default is 4)\n"
142 " -m Minimalistic mode: print as little as possible\n"
143 " -p Do not attempt pretty-printing of known ASN.1 types\n"
144 " -s <skip> Ignore first <skip> bytes of input\n"
145 " -t <hex-string> Decode the given tag[/length] sequence (e.g. -t \"bf20\")\n"
147 "The XML opening tag format is as follows:\n"
148 " <tform O=\"off\" T=\"tag\" TL=\"tl_len\" V=\"{Indefinite|v_len}\" [A=\"type\"] [F]>\n"
150 " tform Which form the value is in: constructed (\"C\", \"I\") or primitive (\"P\")\n"
151 " off Offset of the encoded element in the unber input stream\n"
152 " tag The tag class and value in human readable form\n"
153 " tl_len The length of the TL (BER Tag and Length) encoding\n"
154 " v_len The length of the value (V, encoded by the L), may be \"Indefinite\"\n"
155 " type Likely name of the underlying ASN.1 type (for [UNIVERSAL n] tags)\n"
156 " [F] Indicates that the value was reformatted (pretty-printed)\n"
157 "See the manual page for details\n"
159 /* clang-format on */
163 typedef enum pd_code {
168 static pd_code_e process_deeper(const char *fname, FILE *fp,
169 size_t *offset, int level,
170 ssize_t limit, ber_tlv_len_t *frame_size,
171 ber_tlv_len_t effective_size, int expect_eoc);
172 static void print_TL(int fin, size_t offset, int level, int constr,
173 ssize_t tlen, ber_tlv_tag_t, ber_tlv_len_t,
174 ber_tlv_len_t effective_frame_size);
175 static int print_V(const char *fname, FILE *fp, ber_tlv_tag_t, ber_tlv_len_t);
178 * Open the file and initiate recursive processing.
181 process(const char *fname) {
184 size_t offset = 0; /* Stream decoding position */
185 ber_tlv_len_t frame_size = 0; /* Single frame size */
187 if(strcmp(fname, "-")) {
188 fp = fopen(fname, "rb");
198 * Skip the requested amount of bytes.
200 for(; offset < (size_t)skip_bytes; offset++) {
201 if(fgetc(fp) == -1) {
202 fprintf(stderr, "%s: input source (%zu bytes) "
203 "has less data than \"-s %ld\" switch "
205 fname, offset, skip_bytes);
206 if(fp != stdin) fclose(fp);
212 * Fetch out BER-encoded data until EOF or error.
215 pdc = process_deeper(fname, fp, &offset, 0, -1, &frame_size, 0, 0);
216 } while(pdc == PD_FINISHED && !single_type_decoding);
218 if(fp != stdin) fclose(fp);
220 if(pdc == PD_FAILED) return -1;
225 * Process the TLV recursively.
228 process_deeper(const char *fname, FILE *fp, size_t *offset, int level,
229 ssize_t limit, ber_tlv_len_t *frame_size,
230 ber_tlv_len_t effective_size, int expect_eoc) {
231 unsigned char tagbuf[32];
233 pd_code_e pdc = PD_FINISHED;
234 ber_tlv_tag_t tlv_tag;
235 ber_tlv_len_t tlv_len;
240 ber_tlv_len_t local_esize = 0;
244 if(limit == 0) return PD_FINISHED;
246 if(limit >= 0 && tblen >= limit) {
248 "%s: Too long TL sequence (%ld >= %ld)"
250 "Broken or maliciously constructed file\n",
251 fname, (long)tblen, (long)limit, *offset);
255 /* Get the next byte from the input stream */
258 if(limit > 0 || expect_eoc) {
260 "%s: Unexpected end of file (TL)"
269 tagbuf[tblen++] = ch;
272 * Decode the TLV tag.
274 t_len = ber_fetch_tag(tagbuf, tblen, &tlv_tag);
278 "%s: Fatal error decoding tag"
280 fname, *offset, (long)tblen);
283 /* More data expected */
288 * Decode the TLV length.
290 constr = BER_TLV_CONSTRUCTED(tagbuf);
292 ber_fetch_length(constr, tagbuf + t_len, tblen - t_len, &tlv_len);
296 "%s: Fatal error decoding value length"
298 fname, *offset + t_len);
301 /* More data expected */
305 /* Make sure the T & L decoders took exactly the whole buffer */
306 assert((t_len + l_len) == tblen);
308 if(!expect_eoc || tagbuf[0] || tagbuf[1])
309 print_TL(0, *offset, level, constr, tblen, tlv_tag, tlv_len,
313 /* If limit is set, account for the TL sequence */
314 limit -= (t_len + l_len);
317 if(tlv_len > limit) {
319 "%s: Structure advertizes length (%ld) "
320 "greater than of a parent container (%ld)\n",
321 fname, (long)tlv_len, (long)limit);
326 *offset += t_len + l_len;
327 *frame_size += t_len + l_len;
328 effective_size += t_len + l_len;
329 local_esize += t_len + l_len;
331 if(expect_eoc && !tagbuf[0] && !tagbuf[1]) {
332 /* End of content octets */
333 print_TL(1, *offset - 2, level - 1, 1, 2, 0, -1, effective_size);
338 ber_tlv_len_t dec = 0;
340 * This is a constructed type. Process recursively.
342 printf(">\n"); /* Close the opening tag */
343 if(tlv_len != -1 && limit != -1) {
344 assert(limit >= tlv_len);
346 pdc = process_deeper(fname, fp, offset, level + 1,
347 tlv_len == -1 ? limit : tlv_len, &dec,
348 t_len + l_len, tlv_len == -1);
349 if(pdc == PD_FAILED) return pdc;
351 assert(limit >= dec);
355 effective_size += dec;
359 if(pdc == PD_FINISHED && limit < 0 && !expect_eoc) return pdc;
363 assert(tlv_len >= 0);
364 if(print_V(fname, fp, tlv_tag, tlv_len)) return PD_FAILED;
367 assert(limit >= tlv_len);
371 *frame_size += tlv_len;
372 effective_size += tlv_len;
373 local_esize += tlv_len;
376 print_TL(1, *offset, level, constr, tblen, tlv_tag, tlv_len,
381 /* Report success for a single top level TLV */
382 if(level == 0 && limit == -1 && !expect_eoc) break;
389 print_TL(int fin, size_t offset, int level, int constr, ssize_t tlen,
390 ber_tlv_tag_t tlv_tag, ber_tlv_len_t tlv_len,
391 ber_tlv_len_t effective_size) {
397 while(level-- > 0) fputs(indent_bytes, stdout); /* Print indent */
398 printf(fin ? "</" : "<");
400 printf(constr ? ((tlv_len == -1) ? "I" : "C") : "P");
402 /* Print out the offset of this boundary, even if closing tag */
403 if(!minimalistic) printf(" O=\"%zu\"", offset);
406 ber_tlv_tag_fwrite(tlv_tag, stdout);
409 if(!fin || (tlv_len == -1 && !minimalistic))
410 printf(" TL=\"%ld\"", (long)tlen);
413 printf(" V=\"Indefinite\"");
415 printf(" V=\"%ld\"", (long)tlv_len);
418 if(!minimalistic && BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
420 ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
421 str = ASN_UNIVERSAL_TAG2STR(tvalue);
422 if(str) printf(" A=\"%s\"", str);
426 if(constr && !minimalistic) printf(" L=\"%ld\"", (long)effective_size);
432 * Print the value in binary form, or reformat for pretty-printing.
435 print_V(const char *fname, FILE *fp, ber_tlv_tag_t tlv_tag,
436 ber_tlv_len_t tlv_len) {
437 asn_oid_arc_t *arcs = 0; /* Object identifier arcs */
438 unsigned char *vbuf = 0;
439 asn1p_expr_type_e etype = 0;
440 asn1c_integer_t collector = 0;
441 int special_format = 0;
444 /* Figure out what type is it */
445 if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL && pretty_printing) {
446 ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
447 etype = ASN_UNIVERSAL_TAG2TYPE(tvalue);
451 * Determine how to print the value, either in its native binary form,
452 * encoded with &xNN characters, or using pretty-printing.
453 * The basic string types (including "useful types", like UTCTime)
454 * are excempt from this determination logic, because their alphabets
455 * are subsets of the XML's native UTF-8 encoding.
458 case ASN_BASIC_BOOLEAN:
464 case ASN_BASIC_INTEGER:
465 case ASN_BASIC_ENUMERATED:
466 if((size_t)tlv_len <= sizeof(collector))
471 case ASN_BASIC_OBJECT_IDENTIFIER:
472 case ASN_BASIC_RELATIVE_OID:
473 if(tlv_len > 0 && tlv_len < 128 * 1024 /* VERY long OID! */) {
474 arcs = MALLOC(sizeof(*arcs) * (tlv_len + 1));
476 vbuf = MALLOC(tlv_len + 1);
477 /* Not checking is intentional */
480 case ASN_BASIC_UTCTime:
481 case ASN_BASIC_GeneralizedTime:
482 case ASN_STRING_NumericString:
483 case ASN_STRING_PrintableString:
484 case ASN_STRING_VisibleString:
485 case ASN_STRING_IA5String:
486 case ASN_STRING_UTF8String:
487 break; /* Directly compatible with UTF-8 */
488 case ASN_STRING_BMPString:
489 case ASN_STRING_UniversalString:
490 break; /* Not directly compatible with UTF-8 */
492 /* Conditionally compatible with UTF-8 */
493 if(((etype & ASN_STRING_MASK) || (etype == ASN_BASIC_OCTET_STRING) ||
495 * AUTOMATIC TAGS or IMPLICIT TAGS in effect,
496 * Treat this primitive type as OCTET_STRING.
498 (BER_TAG_CLASS(tlv_tag) != ASN_TAG_CLASS_UNIVERSAL
500 && (tlv_len > 0 && tlv_len < 128 * 1024)) {
501 vbuf = MALLOC(tlv_len + 1);
502 /* Not checking is intentional */
507 /* If collection vbuf is present, defer printing the F flag. */
508 if(!vbuf) printf(special_format ? " F>" : ">");
511 * Print the value in binary or text form,
512 * or collect the bytes into vbuf.
514 for(i = 0; i < tlv_len; i++) {
517 fprintf(stderr, "%s: Unexpected end of file (V)\n", fname);
518 if(vbuf) FREEMEM(vbuf);
519 if(arcs) FREEMEM(arcs);
523 case ASN_BASIC_UTCTime:
524 case ASN_BASIC_GeneralizedTime:
525 case ASN_STRING_NumericString:
526 case ASN_STRING_PrintableString:
527 case ASN_STRING_VisibleString:
528 case ASN_STRING_IA5String:
529 case ASN_STRING_UTF8String:
532 if(((etype == ASN_STRING_UTF8String) || !(ch & 0x80))
541 printf("&#x%02x;", ch);
544 case ASN_BASIC_BOOLEAN:
553 printf("<true value=\"&#x%02x\"/>", ch);
556 case ASN_BASIC_INTEGER:
557 case ASN_BASIC_ENUMERATED:
559 collector = collector * 256 + ch;
561 collector = (int)(signed char)ch;
567 printf("&#x%02x;", ch);
572 /* Do post-processing */
574 case ASN_BASIC_INTEGER:
575 case ASN_BASIC_ENUMERATED:
576 printf("%s", asn1p_itoa(collector));
578 case ASN_BASIC_OBJECT_IDENTIFIER:
580 OBJECT_IDENTIFIER_t oid = {0, 0};
586 arcno = OBJECT_IDENTIFIER_get_arcs(&oid, arcs, tlv_len + 1);
588 assert(arcno <= (tlv_len + 1));
590 for(i = 0; i < arcno; i++) {
592 printf("%" PRIu32, arcs[i]);
599 case ASN_BASIC_RELATIVE_OID:
607 arcno = RELATIVE_OID_get_arcs(&oid, arcs, tlv_len);
609 assert(arcno <= tlv_len);
611 for(i = 0; i < arcno; i++) {
613 printf("%" PRIu32, arcs[i]);
625 * If the buffer was not consumed, print it out.
626 * It might be an OCTET STRING or other primitive type,
627 * which might actually be printable, but we need to figure it out.
633 * Check whether the data could be represented as text
635 binary = -1 * (tlv_len >> 3); /* Threshold is 12.5% binary */
636 for(i = 0; i < tlv_len; i++) {
646 if(vbuf[i] < 0x20 || vbuf[i] >= 0x7f)
647 if(++binary > 0) /* Way too many */
654 for(i = 0; i < tlv_len; i++) {
655 if(binary > 0 || vbuf[i] < 0x20 || vbuf[i] >= 0x7f
656 || vbuf[i] == 0x26 /* '&' */
657 || vbuf[i] == 0x3c /* '<' */
658 || vbuf[i] == 0x3e /* '>' */
660 printf("&#x%02x;", vbuf[i]);
662 printf("%c", vbuf[i]);
667 if(arcs) FREEMEM(arcs);
673 decode_tlv_from_string(const char *datastring) {
674 unsigned char *data, *dp;
675 size_t dsize; /* Data size */
677 ber_tlv_tag_t tlv_tag;
678 ber_tlv_len_t tlv_len;
682 dsize = strlen(datastring) + 1;
683 dp = data = CALLOC(1, dsize);
686 for(half = 0, p = datastring; *p; p++) {
687 /* clang-format off */
689 case '0': case '1': case '2': case '3': case '4':
690 case '5': case '6': case '7': case '8': case '9':
691 *dp |= *p - '0'; break;
692 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
693 *dp |= *p - 'A' + 10; break;
694 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
695 *dp |= *p - 'a' + 10; break;
696 case ' ': case '\t': case '\r': case '\n':
699 fprintf(stderr, "Unexpected symbols in data string:\n");
700 fprintf(stderr, "%s\n", datastring);
701 for(dp = data; datastring < p; datastring++, dp++)
704 fprintf(stderr, "%s^ <- here\n", (char *)data);
707 /* clang-format on */
715 assert((size_t)(dp - data) <= dsize);
719 for(dp = data; dp < data + dsize; dp++) printf("%02X", *dp);
722 len = ber_fetch_tag(data, dsize, &tlv_tag);
725 fprintf(stderr, "TAG: Fatal error decoding tag\n");
728 fprintf(stderr, "TAG: More data expected\n");
732 ber_tlv_tag_fwrite(tlv_tag, stdout);
733 if(BER_TLV_CONSTRUCTED(data)) {
734 printf(" (constructed)");
735 } else if(dsize >= 2 && data[0] == 0 && data[1] == 0) {
736 printf(" (end-of-content)");
738 printf(" (primitive)");
740 if(BER_TAG_CLASS(tlv_tag) == ASN_TAG_CLASS_UNIVERSAL) {
742 ber_tlv_tag_t tvalue = BER_TAG_VALUE(tlv_tag);
743 str = ASN_UNIVERSAL_TAG2STR(tvalue);
744 if(str) printf(" \"%s\"", str);
749 if(dsize > (size_t)len) {
750 len = ber_fetch_length(BER_TLV_CONSTRUCTED(data), data + len,
751 dsize - len, &tlv_len);
754 fprintf(stderr, "LEN: Fatal error decoding length\n");
757 fprintf(stderr, "LEN: More data expected\n");
760 if(tlv_len == (ber_tlv_len_t)-1)
761 printf("LEN: Indefinite length encoding\n");
763 printf("LEN: %ld bytes\n", (long)tlv_len);
774 ber_check_tags(const asn_codec_ctx_t *opt_codec_ctx,
775 const asn_TYPE_descriptor_t *td, asn_struct_ctx_t *opt_ctx,
776 const void *ptr, size_t size, int tag_mode, int last_tag_form,
777 ber_tlv_len_t *last_length, int *opt_tlv_form) {
778 asn_dec_rval_t rv = {0, 0};
792 der_write_tags(const asn_TYPE_descriptor_t *td, size_t slen, int tag_mode,
793 int last_tag_form, ber_tlv_tag_t tag,
794 asn_app_consume_bytes_f *cb, void *app_key) {
806 xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx, asn_struct_ctx_t *ctx,
807 void *struct_key, const char *xml_tag, const void *buf_ptr,
809 int (*otd)(void *struct_key, const void *chunk_buf,
811 ssize_t (*br)(void *struct_key, const void *chunk_buf,
812 size_t chunk_size, int have_more)) {
813 asn_dec_rval_t rv = {0, 0};
826 xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
833 OCTET_STRING_compare(const asn_TYPE_descriptor_t *td, const void *a,
842 asn_random_between(intmax_t a, intmax_t b) {