textual
[RRG-proxmark3.git] / client / src / crypto / asn1dump.c
blob84ff1d4a232541a03ffc6cec05a15e51111e5aa6
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // asn.1 dumping
9 //-----------------------------------------------------------------------------
10 #define _POSIX_C_SOURCE 200809L // need for strnlen()
11 #include "asn1dump.h"
13 #include "commonutil.h" // ARRAYLEN
15 #include <ctype.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <jansson.h>
20 #include <mbedtls/asn1.h>
21 #include <mbedtls/oid.h>
22 #include "emv/emv_tags.h"
23 #include "emv/emvjson.h"
24 #include "util.h"
25 #include "proxmark3.h"
26 #include "fileutils.h"
27 #include "pm3_cmd.h"
29 enum asn1_tag_t {
30 ASN1_TAG_GENERIC,
31 ASN1_TAG_BOOLEAN,
32 ASN1_TAG_INTEGER,
33 ASN1_TAG_STRING,
34 ASN1_TAG_OCTET_STRING,
35 ASN1_TAG_UTC_TIME,
36 ASN1_TAG_STR_TIME,
37 ASN1_TAG_OBJECT_ID,
38 ASN1_TAG_HEX,
41 struct asn1_tag {
42 tlv_tag_t tag;
43 const char *name;
44 enum asn1_tag_t type;
45 // const void *data;
48 static const struct asn1_tag asn1_tags[] = {
49 // internal
50 { 0x00, "elem", ASN1_TAG_GENERIC }, // PRIMITIVE
51 { 0x20, "CONSTRUCTED", ASN1_TAG_GENERIC }, // CONSTRUCTED, the sequence has multiple elements
52 { 0x80, "CONTEXT SPECIFIC", ASN1_TAG_GENERIC },
54 // ASN.1
55 { 0x01, "BOOLEAN", ASN1_TAG_BOOLEAN },
56 { 0x02, "INTEGER", ASN1_TAG_INTEGER },
57 { 0x03, "BIT STRING", ASN1_TAG_GENERIC },
58 { 0x04, "OCTET STRING", ASN1_TAG_OCTET_STRING },
59 { 0x05, "NULL", ASN1_TAG_GENERIC },
60 { 0x06, "OBJECT IDENTIFIER", ASN1_TAG_OBJECT_ID },
61 { 0x07, "OBJECT DESCRIPTOR", ASN1_TAG_GENERIC },
62 { 0x08, "EXTERNAL", ASN1_TAG_GENERIC },
63 { 0x09, "REAL", ASN1_TAG_GENERIC },
64 { 0x0A, "ENUMERATED", ASN1_TAG_GENERIC },
65 { 0x0B, "EMBEDDED_PDV", ASN1_TAG_GENERIC },
66 { 0x0C, "UTF8String", ASN1_TAG_STRING },
67 { 0x10, "SEQUENCE", ASN1_TAG_GENERIC },
68 { 0x11, "SET", ASN1_TAG_GENERIC },
69 { 0x12, "NumericString", ASN1_TAG_STRING },
70 { 0x13, "PrintableString", ASN1_TAG_STRING },
71 { 0x14, "T61String", ASN1_TAG_GENERIC },
72 { 0x15, "VideotexString", ASN1_TAG_GENERIC },
73 { 0x16, "IA5String", ASN1_TAG_GENERIC },
74 { 0x17, "UTCTime", ASN1_TAG_UTC_TIME },
75 { 0x18, "GeneralizedTime", ASN1_TAG_STR_TIME },
76 { 0x19, "GraphicString", ASN1_TAG_GENERIC },
77 { 0x1A, "VisibleString", ASN1_TAG_STRING },
78 { 0x1B, "GeneralString", ASN1_TAG_STRING },
79 { 0x1C, "UniversalString", ASN1_TAG_STRING },
80 { 0x1E, "BMPString", ASN1_TAG_GENERIC },
81 { 0x30, "SEQUENCE", ASN1_TAG_GENERIC },
82 { 0x31, "SET", ASN1_TAG_GENERIC },
83 { 0xa0, "[0]", ASN1_TAG_GENERIC },
84 { 0xa1, "[1]", ASN1_TAG_GENERIC },
85 { 0xa2, "[2]", ASN1_TAG_GENERIC },
86 { 0xa3, "[3]", ASN1_TAG_GENERIC },
87 { 0xa4, "[4]", ASN1_TAG_GENERIC },
88 { 0xa5, "[5]", ASN1_TAG_GENERIC },
92 static int asn1_sort_tag(tlv_tag_t tag) {
93 return (int)(tag >= 0x100 ? tag : tag << 8);
96 static int asn1_tlv_compare(const void *a, const void *b) {
97 const struct tlv *tlv = a;
98 const struct asn1_tag *tag = b;
100 return asn1_sort_tag(tlv->tag) - (asn1_sort_tag(tag->tag));
103 static const struct asn1_tag *asn1_get_tag(const struct tlv *tlv) {
104 struct asn1_tag *tag = bsearch(tlv, asn1_tags, ARRAYLEN(asn1_tags),
105 sizeof(asn1_tags[0]), asn1_tlv_compare);
107 return tag ? tag : &asn1_tags[0];
110 static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag *tag, int level, bool longyear, bool *needdump) {
111 int len = tlv->len;
112 *needdump = false;
114 int startindx = longyear ? 4 : 2;
116 if (len > 4) {
117 PrintAndLogEx(NORMAL, " value: '" NOLF);
118 while (true) {
119 // year
120 if (longyear == false)
121 PrintAndLogEx(NORMAL, "20" NOLF);
123 PrintAndLogEx(NORMAL, "%s-" NOLF, sprint_hex(tlv->value, startindx));
125 if (len < startindx + 2)
126 break;
128 // month
129 PrintAndLogEx(NORMAL, "%02x%02x-" NOLF, tlv->value[startindx], tlv->value[startindx + 1]);
130 if (len < startindx + 4)
131 break;
133 // day
134 PrintAndLogEx(NORMAL, "%02x%02x " NOLF, tlv->value[startindx + 2], tlv->value[startindx + 3]);
135 if (len < startindx + 6)
136 break;
137 // hour
138 PrintAndLogEx(NORMAL, "%02x%02x:" NOLF, tlv->value[startindx + 4], tlv->value[startindx + 5]);
139 if (len < startindx + 8)
140 break;
141 // min
142 PrintAndLogEx(NORMAL, "%02x%02x:" NOLF, tlv->value[startindx + 6], tlv->value[startindx + 7]);
143 if (len < startindx + 10)
144 break;
145 // sec
146 PrintAndLogEx(NORMAL, "%02x%02x" NOLF, tlv->value[startindx + 8], tlv->value[startindx + 9]);
147 if (len < startindx + 11)
148 break;
149 // time zone
150 PrintAndLogEx(NORMAL, " zone: %.*s" NOLF, len - 10 - (longyear ? 4 : 2), &tlv->value[startindx + 10]);
151 break;
153 PrintAndLogEx(NORMAL, "'");
154 } else {
155 PrintAndLogEx(NORMAL, "");
156 *needdump = true;
160 static void asn1_tag_dump_string(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
161 PrintAndLogEx(NORMAL, " value: '%s'", sprint_hex(tlv->value, tlv->len));
164 static void asn1_tag_dump_hex(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
165 PrintAndLogEx(NORMAL, " value: '%s'", sprint_hex_inrow(tlv->value, tlv->len));
168 static void asn1_tag_dump_octet_string(const struct tlv *tlv, const struct asn1_tag *tag, int level, bool *needdump) {
169 *needdump = false;
170 for (size_t i = 0; i < tlv->len; i++)
171 if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])) {
172 *needdump = true;
173 break;
176 if (*needdump) {
177 PrintAndLogEx(NORMAL, "");
178 } else {
179 PrintAndLogEx(NORMAL, " " NOLF);
180 asn1_tag_dump_string(tlv, tag, level);
184 static unsigned long asn1_value_integer(const struct tlv *tlv, unsigned start, unsigned end) {
185 unsigned long ret = 0;
186 unsigned i;
188 if (end > tlv->len * 2)
189 return ret;
190 if (start >= end)
191 return ret;
193 if (start & 1) {
194 ret += tlv->value[start / 2] & 0xf;
195 i = start + 1;
196 } else
197 i = start;
199 for (; i < end - 1; i += 2) {
200 ret *= 10;
201 ret += tlv->value[i / 2] >> 4;
202 ret *= 10;
203 ret += tlv->value[i / 2] & 0xf;
206 if (end & 1) {
207 ret *= 10;
208 ret += tlv->value[end / 2] >> 4;
211 return ret;
214 static void asn1_tag_dump_boolean(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
215 PrintAndLogEx(NORMAL, "%*s" NOLF, (level * 4), " ");
216 if (tlv->len > 0) {
217 PrintAndLogEx(NORMAL, " value: %s", tlv->value[0] ? "true" : "false");
218 } else {
219 PrintAndLogEx(NORMAL, "n/a");
223 static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
224 PrintAndLogEx(NORMAL, "%*s" NOLF, (level * 4), " ");
225 if (tlv->len == 4) {
226 int32_t val = 0;
227 for (size_t i = 0; i < tlv->len; i++) {
228 val = (val << 8) + tlv->value[i];
230 PrintAndLogEx(NORMAL, " value4b: %d", val);
231 return;
233 PrintAndLogEx(NORMAL, " value: %lu", asn1_value_integer(tlv, 0, tlv->len * 2));
236 static char *asn1_oid_description(const char *oid, bool with_group_desc) {
237 json_error_t error;
238 json_t *root = NULL;
239 static char res[300];
240 memset(res, 0x00, sizeof(res));
242 char *path;
243 if (searchFile(&path, RESOURCES_SUBDIR, "oids", ".json", false) != PM3_SUCCESS) {
244 return NULL;
247 // load `oids.json`
248 root = json_load_file(path, 0, &error);
249 free(path);
251 if (!root || !json_is_object(root)) {
252 goto error;
255 json_t *elm = json_object_get(root, oid);
256 if (!elm) {
257 goto error;
260 if (JsonLoadStr(elm, "$.d", res))
261 goto error;
263 char strext[300] = {0};
264 if (!JsonLoadStr(elm, "$.c", strext)) {
265 strcat(res, " (");
266 strcat(res, strext);
267 strcat(res, ")");
270 json_decref(root);
271 return res;
273 error:
274 if (root)
275 json_decref(root);
276 return NULL;
279 static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag *tag, int level) {
281 mbedtls_asn1_buf asn1_buf;
282 asn1_buf.len = tlv->len;
283 asn1_buf.p = (uint8_t *)tlv->value;
284 char pstr[300];
285 mbedtls_oid_get_numeric_string(pstr, sizeof(pstr), &asn1_buf);
287 PrintAndLogEx(INFO, "%*s %s" NOLF, (level * 4), " ", pstr);
289 char *jsondesc = asn1_oid_description(pstr, true);
290 if (jsondesc) {
291 PrintAndLogEx(NORMAL, " - %s" NOLF, jsondesc);
292 } else {
293 const char *ppstr;
294 mbedtls_oid_get_attr_short_name(&asn1_buf, &ppstr);
295 if (ppstr && strnlen(ppstr, 1)) {
296 PrintAndLogEx(NORMAL, " (%s)", ppstr);
297 return;
299 mbedtls_oid_get_sig_alg_desc(&asn1_buf, &ppstr);
300 if (ppstr && strnlen(ppstr, 1)) {
301 PrintAndLogEx(NORMAL, " (%s)", ppstr);
302 return;
304 mbedtls_oid_get_extended_key_usage(&asn1_buf, &ppstr);
305 if (ppstr && strnlen(ppstr, 1)) {
306 PrintAndLogEx(NORMAL, " (%s)", ppstr);
307 return;
310 PrintAndLogEx(NORMAL, "");
313 bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) {
314 if (tlv == NULL) {
315 PrintAndLogEx(FAILED, "NULL\n");
316 return false;
319 const struct asn1_tag *tag = asn1_get_tag(tlv);
322 if ((tlv->tag & 0x20) == 0x20 ) {
323 } else if ((tlv->tag & 0x80) == 0x80 ) {
324 } else {
328 PrintAndLogEx(INFO,
329 "%*s-- %2x [%02zx] '"_YELLOW_("%s") "'" NOLF
330 , (level * 4)
331 , " "
332 , tlv->tag
333 , tlv->len
334 , tag->name
337 switch (tag->type) {
338 case ASN1_TAG_GENERIC:
339 PrintAndLogEx(NORMAL, "");
340 // maybe print number of elements?
341 break;
342 case ASN1_TAG_STRING:
343 asn1_tag_dump_string(tlv, tag, level);
344 *candump = false;
345 break;
346 case ASN1_TAG_OCTET_STRING:
347 asn1_tag_dump_octet_string(tlv, tag, level, candump);
348 break;
349 case ASN1_TAG_BOOLEAN:
350 asn1_tag_dump_boolean(tlv, tag, level);
351 *candump = false;
352 break;
353 case ASN1_TAG_INTEGER:
354 asn1_tag_dump_integer(tlv, tag, level);
355 *candump = false;
356 break;
357 case ASN1_TAG_UTC_TIME:
358 asn1_tag_dump_str_time(tlv, tag, level, false, candump);
359 break;
360 case ASN1_TAG_STR_TIME:
361 asn1_tag_dump_str_time(tlv, tag, level, true, candump);
362 break;
363 case ASN1_TAG_OBJECT_ID:
364 asn1_tag_dump_object_id(tlv, tag, level);
365 *candump = false;
366 break;
367 case ASN1_TAG_HEX:
368 asn1_tag_dump_hex(tlv, tag, level);
369 *candump = false;
370 break;
373 return true;