Created SILC Crypto Toolkit git repository.
[crypto.git] / apps / asn1utils / asn1dump.c
1 /*
2
3   asn1dump.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silcruntime.h"
21 #include "silccrypto.h"
22
23 SilcBool hexdump = FALSE;
24 SilcBool dec_base64 = FALSE;
25 SilcBool parse_all = FALSE;
26 SilcBool ignore_header = FALSE;
27
28 const char *asn1_tag_name(SilcAsn1Tag tag)
29 {
30   switch (tag) {
31   case SILC_ASN1_TAG_CHOICE:
32     return "choice";
33   case SILC_ASN1_TAG_ANY:
34     return "any";
35   case SILC_ASN1_TAG_ANY_PRIMITIVE:
36     return "any primitive";
37   case SILC_ASN1_TAG_SEQUENCE_OF:
38     return "sequence of";
39   case SILC_ASN1_TAG_SEQUENCE:
40     return "sequence";
41   case SILC_ASN1_TAG_SET:
42     return "set";
43   case SILC_ASN1_TAG_INTEGER:
44     return "integer";
45   case SILC_ASN1_TAG_SHORT_INTEGER:
46     return "short integer";
47   case SILC_ASN1_TAG_OID:
48     return "oid";
49   case SILC_ASN1_TAG_BOOLEAN:
50     return "boolean";
51   case SILC_ASN1_TAG_OCTET_STRING:
52     return "octet-string";
53   case SILC_ASN1_TAG_BIT_STRING:
54     return "bit-string";
55   case SILC_ASN1_TAG_NULL:
56     return "null";
57   case SILC_ASN1_TAG_ENUM:
58     return "enum";
59   case SILC_ASN1_TAG_UTC_TIME:
60     return "utc-time";
61   case SILC_ASN1_TAG_GENERALIZED_TIME:
62     return "generalized-time";
63   case SILC_ASN1_TAG_UTF8_STRING:
64     return "utf8-string";
65   case SILC_ASN1_TAG_NUMERIC_STRING:
66     return "numeric-string";
67   case SILC_ASN1_TAG_PRINTABLE_STRING:
68     return "printable-string";
69   case SILC_ASN1_TAG_IA5_STRING:
70     return "ia5-string";
71   case SILC_ASN1_TAG_VISIBLE_STRING:
72     return "visible-string";
73   case SILC_ASN1_TAG_UNIVERSAL_STRING:
74     return "universal-string";
75   case SILC_ASN1_TAG_UNRESTRICTED_STRING:
76     return "unrestricted-string";
77   case SILC_ASN1_TAG_BMP_STRING:
78     return "bmp-string";
79   case SILC_ASN1_TAG_ODE:
80     return "ode";
81   case SILC_ASN1_TAG_ETI:
82     return "eti";
83   case SILC_ASN1_TAG_REAL:
84     return "real";
85   case SILC_ASN1_TAG_EMBEDDED:
86     return "embedded";
87   case SILC_ASN1_TAG_ROI:
88     return "roi";
89   case SILC_ASN1_TAG_TELETEX_STRING:
90     return "teletex-string";
91   case SILC_ASN1_TAG_VIDEOTEX_STRING:
92     return "videotex-string";
93   case SILC_ASN1_TAG_GRAPHIC_STRING:
94     return "graphic-string";
95   case SILC_ASN1_TAG_GENERAL_STRING:
96     return "general-string";
97   default:
98     break;
99   }
100   return "unknown";
101 }
102
103 int asn1_dump(SilcAsn1 asn1, SilcBuffer src, int depth)
104 {
105   SilcBool ret = FALSE;
106   SilcBerEncoding renc;
107   SilcUInt32 rtag;
108   const unsigned char *rdata;
109   SilcBufferStruct buf;
110   SilcBerClass rclass;
111   SilcUInt32 rdata_len, len = 0;
112   SilcBool rindef;
113   char indent[256];
114
115   memset(indent, 0, sizeof(indent));
116
117   while (silc_buffer_len(src)) {
118     /* Decode the BER block */
119     ret = silc_ber_decode(src, &rclass, &renc, &rtag, &rdata,
120                           &rdata_len, &rindef, &len);
121     if (!ret) {
122       fprintf(stderr, "Error: Cannot parse BER block, malformed ASN.1 data\n");
123       return -1;
124     }
125
126     /* If class is 0, encoding 0, tag 0 and data length 0 ignore them
127        as they are zero bytes, unless user wants to see them */
128     if (rclass == 0 && renc == 0 && rtag == 0 && rdata_len == 0 &&
129         !parse_all) {
130       if (len && silc_buffer_len(src) >= len)
131         silc_buffer_pull(src, len);
132       continue;
133     }
134
135     if (depth)
136       memset(indent, 32, depth);
137
138     fprintf(stdout, "%04d: %s%s [%d] %s %s %s", depth, indent,
139             asn1_tag_name(rtag), (int)rtag,
140             rclass == SILC_BER_CLASS_UNIVERSAL   ? "univ" :
141             rclass == SILC_BER_CLASS_APPLICATION ? "appl" :
142             rclass == SILC_BER_CLASS_CONTEXT     ? "cont" : "priv",
143             renc == SILC_BER_ENC_PRIMITIVE ? "primit" : "constr",
144             rindef ? "indef" : "defin");
145
146     if (rtag != SILC_ASN1_TAG_SEQUENCE &&
147         rtag != SILC_ASN1_TAG_SET &&
148         rtag != SILC_ASN1_TAG_SEQUENCE_OF) {
149       if (hexdump) {
150         fprintf(stdout, " [len %lu]\n", rdata_len);
151         silc_hexdump(rdata, rdata_len, stdout);
152       } else {
153         fprintf(stdout, "\n");
154       }
155     } else {
156       fprintf(stdout, "\n");
157     }
158
159     if (renc == SILC_BER_ENC_PRIMITIVE)
160       len = len + rdata_len;
161     else
162       len = len;
163
164     if (len && silc_buffer_len(src) >= len)
165       silc_buffer_pull(src, len);
166
167     /* Decode sequences and sets recursively */
168     if ((rtag == SILC_ASN1_TAG_SEQUENCE ||
169          rtag == SILC_ASN1_TAG_SET ||
170          rtag == SILC_ASN1_TAG_SEQUENCE_OF) &&
171         depth + 1 < sizeof(indent) - 1) {
172       silc_buffer_set(&buf, (unsigned char *)rdata, rdata_len);
173       if (silc_buffer_len(src) >= rdata_len)
174         silc_buffer_pull(src, rdata_len);
175       if (asn1_dump(asn1, &buf, depth + 1) < 0)
176         return -1;
177       if (silc_buffer_len(src) == 0)
178         return 0;
179     }
180   }
181
182   return 0;
183 }
184
185 void usage(void)
186 {
187     fprintf(stdout, ""
188 "Usage: asn1dump [OPTIONS] FILE\n"
189 "\n"
190 "Operation modes:\n"
191 "  -h        Print this help, then exit\n"
192 "  -x        HEX dump ASN.1 data\n"
193 "  -b        Remove Base64 encoding before parsing\n"
194 "  -i        Remove file header/footer that has at least four '-' characters\n"
195 "  -a        Parse all data, including possible trailing zeroes\n"
196 "\n"
197 "ASN.1 classes:\n"
198 "  univ      Universal\n"
199 "  appl      Application\n"
200 "  cont      Context\n"
201 "  priv      Private\n"
202 "\n"
203 "ASN.1 length types:\n"
204 "  defin     Definitive\n"
205 "  indef     Indefinitive\n"
206 "\n"
207 "ASN.1 encoding types:\n"
208 "  primit    Primitive\n"
209 "  constr    Constructed\n"
210 "\n"
211             );
212 }
213
214 int main(int argc, char **argv)
215 {
216   int opt, ret, i;
217   SilcAsn1 asn1;
218   SilcBufferStruct buf;
219   unsigned char *data, *tmp;
220   SilcUInt32 data_len;
221
222   if (argc < 2) {
223     usage();
224     return 1;
225   }
226
227   while ((opt = getopt(argc, argv, "hxbai")) != EOF) {
228     switch (opt) {
229     case 'h':
230       usage();
231       return 1;
232       break;
233
234     case 'x':
235       hexdump = TRUE;
236       break;
237
238     case 'b':
239       dec_base64 = TRUE;
240       break;
241
242     case 'i':
243       ignore_header = TRUE;
244       break;
245
246     case 'a':
247       parse_all = TRUE;
248       break;
249
250     default:
251       usage();
252       return 1;
253     }
254   }
255
256   data = tmp = silc_file_readfile(argv[argc - 1], &data_len, NULL);
257   if (!data) {
258     fprintf(stderr, "Error: Cannot read file '%s': %s\n", argv[argc - 1],
259             silc_errno_string(silc_errno));
260     return 1;
261   }
262
263   silc_buffer_set(&buf, data, data_len);
264
265   if (ignore_header) {
266     SilcBool header = FALSE;
267     for (i = 0; i < data_len; i++) {
268       if (data_len > i + 4 &&
269           data[i    ] == '-' && data[i + 1] == '-' &&
270           data[i + 2] == '-' && data[i + 3] == '-') {
271
272         if (data_len > i + 5 && (data[i + 4] == '\r' ||
273                                  tmp[i + 4] == '\n')) {
274           /* End of line, header */
275           if (data_len > i + 6 && data[i + 4] == '\r' &&
276               data[i + 5] == '\n')
277             i++;
278           i += 5;
279           silc_buffer_pull(&buf, i);
280           header = TRUE;
281         } else if (i > 0 && data_len > i + 5 && data[i + 4] != '-' &&
282                    header) {
283           /* Start of line, footer */
284           silc_buffer_push_tail(&buf, silc_buffer_truelen(&buf) - i);
285           break;
286         }
287       }
288     }
289   }
290
291   if (dec_base64) {
292     data = silc_base64_decode(NULL, silc_buffer_data(&buf),
293                               silc_buffer_len(&buf), &data_len);
294     if (!data) {
295       fprintf(stderr, "Error: Cannot decode Base64 encoding\n");
296       return 1;
297     }
298     silc_buffer_set(&buf, data, data_len);
299     silc_free(tmp);
300   }
301
302   asn1 = silc_asn1_alloc(NULL);
303
304   ret = asn1_dump(asn1, &buf, 0);
305
306   silc_asn1_free(asn1);
307   silc_free(data);
308
309   return ret;
310 }