Fix format-truncation warning, missing string.h inclusion and strnlen warning (#723)
[proxmark3-svn] / client / crypto / asn1dump.c
CommitLineData
6b882a39
OM
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
994f21fe
F
11#define _POSIX_C_SOURCE 200809L // need for strnlen()
12
6b882a39
OM
13#include "asn1dump.h"
14#include <ctype.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <stdio.h>
994f21fe 18#include <string.h>
6b882a39
OM
19#include <jansson.h>
20#include <mbedtls/asn1.h>
21#include <mbedtls/oid.h>
22#include "emv/emv_tags.h"
23#include "emv/dump.h"
24#include "emv/emvjson.h"
25#include "util.h"
26#include "proxmark3.h"
27
28#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, " ");}
29
30enum asn1_tag_t {
31 ASN1_TAG_GENERIC,
32 ASN1_TAG_BOOLEAN,
33 ASN1_TAG_INTEGER,
34 ASN1_TAG_STRING,
35 ASN1_TAG_OCTET_STRING,
36 ASN1_TAG_UTC_TIME,
37 ASN1_TAG_STR_TIME,
38 ASN1_TAG_OBJECT_ID,
39};
40
41struct asn1_tag {
42 tlv_tag_t tag;
43 char *name;
44 enum asn1_tag_t type;
45 const void *data;
46};
47
48static const struct asn1_tag asn1_tags[] = {
49 // internal
50 { 0x00 , "Unknown ???" },
51
52 // ASN.1
53 { 0x01, "BOOLEAN", ASN1_TAG_BOOLEAN },
54 { 0x02, "INTEGER", ASN1_TAG_INTEGER },
55 { 0x03, "BIT STRING" },
56 { 0x04, "OCTET STRING", ASN1_TAG_OCTET_STRING},
57 { 0x05, "NULL" },
58 { 0x06, "OBJECT IDENTIFIER", ASN1_TAG_OBJECT_ID },
59 { 0x07, "OBJECT DESCRIPTOR" },
60 { 0x08, "EXTERNAL" },
61 { 0x09, "REAL" },
62 { 0x0A, "ENUMERATED" },
63 { 0x0B, "EMBEDDED_PDV" },
64 { 0x0C, "UTF8String", ASN1_TAG_STRING },
65 { 0x10, "SEQUENCE" },
66 { 0x11, "SET" },
67 { 0x12, "NumericString", ASN1_TAG_STRING },
68 { 0x13, "PrintableString", ASN1_TAG_STRING },
69 { 0x14, "T61String" },
70 { 0x15, "VideotexString" },
71 { 0x16, "IA5String" },
72 { 0x17, "UTCTime", ASN1_TAG_UTC_TIME },
73 { 0x18, "GeneralizedTime", ASN1_TAG_STR_TIME },
74 { 0x19, "GraphicString" },
75 { 0x1A, "VisibleString", ASN1_TAG_STRING },
76 { 0x1B, "GeneralString", ASN1_TAG_STRING },
77 { 0x1C, "UniversalString", ASN1_TAG_STRING },
78 { 0x1E, "BMPString" },
79 { 0x30, "SEQUENCE" },
80 { 0x31, "SET" },
81 { 0xa0, "[0]" },
82 { 0xa1, "[1]" },
83 { 0xa2, "[2]" },
84 { 0xa3, "[3]" },
85 { 0xa4, "[4]" },
86 { 0xa5, "[5]" },
87};
88
89static int asn1_sort_tag(tlv_tag_t tag) {
90 return (int)(tag >= 0x100 ? tag : tag << 8);
91}
92
93static int asn1_tlv_compare(const void *a, const void *b) {
94 const struct tlv *tlv = a;
95 const struct asn1_tag *tag = b;
96
97 return asn1_sort_tag(tlv->tag) - (asn1_sort_tag(tag->tag));
98}
99
100static const struct asn1_tag *asn1_get_tag(const struct tlv *tlv) {
101 struct asn1_tag *tag = bsearch(tlv, asn1_tags, sizeof(asn1_tags) / sizeof(asn1_tags[0]),
102 sizeof(asn1_tags[0]), asn1_tlv_compare);
103
104 return tag ? tag : &asn1_tags[0];
105}
106
107static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level, bool longyear, bool *needdump){
108 int len = tlv->len;
109 *needdump = false;
110
111 int startindx = longyear ? 4 : 2;
112
113 if (len > 4) {
114 fprintf(f, "\tvalue: '");
115 while (true) {
116 // year
117 if (!longyear)
118 fprintf(f, "20");
119 fwrite(tlv->value, 1, longyear ? 4 : 2, f);
120 fprintf(f, "-");
121 if (len < startindx + 2)
122 break;
123 // month
124 fwrite(&tlv->value[startindx], 1, 2, f);
125 fprintf(f, "-");
126 if (len < startindx + 4)
127 break;
128 // day
129 fwrite(&tlv->value[startindx + 2], 1, 2, f);
130 fprintf(f, " ");
131 if (len < startindx + 6)
132 break;
133 // hour
134 fwrite(&tlv->value[startindx + 4], 1, 2, f);
135 fprintf(f, ":");
136 if (len < startindx + 8)
137 break;
138 // min
139 fwrite(&tlv->value[startindx + 6], 1, 2, f);
140 fprintf(f, ":");
141 if (len < startindx + 10)
142 break;
143 // sec
144 fwrite(&tlv->value[startindx + 8], 1, 2, f);
145 if (len < startindx + 11)
146 break;
147 // time zone
148 fprintf(f, " zone: %.*s", len - 10 - (longyear ? 4 : 2), &tlv->value[startindx + 10]);
149
150 break;
151 }
152 fprintf(f, "'\n");
153 } else {
154 fprintf(f, "\n");
155 *needdump = true;
156 }
157}
158
159static void asn1_tag_dump_string(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level){
160 fprintf(f, "\tvalue: '");
161 fwrite(tlv->value, 1, tlv->len, f);
162 fprintf(f, "'\n");
163}
164
165static void asn1_tag_dump_octet_string(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level, bool *needdump){
166 *needdump = false;
167 for (int i = 0; i < tlv->len; i++)
168 if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])){
169 *needdump = true;
170 break;
171 }
172
173 if (*needdump) {
174 fprintf(f, "'\n");
175 } else {
176 fprintf(f, "\t\t");
177 asn1_tag_dump_string(tlv, tag, f, level);
178 }
179}
180
181static unsigned long asn1_value_integer(const struct tlv *tlv, unsigned start, unsigned end) {
182 unsigned long ret = 0;
183 int i;
184
185 if (end > tlv->len * 2)
186 return ret;
187 if (start >= end)
188 return ret;
189
190 if (start & 1) {
191 ret += tlv->value[start/2] & 0xf;
192 i = start + 1;
193 } else
194 i = start;
195
196 for (; i < end - 1; i += 2) {
197 ret *= 10;
198 ret += tlv->value[i/2] >> 4;
199 ret *= 10;
200 ret += tlv->value[i/2] & 0xf;
201 }
202
203 if (end & 1) {
204 ret *= 10;
205 ret += tlv->value[end/2] >> 4;
206 }
207
208 return ret;
209}
210
211static void asn1_tag_dump_boolean(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level) {
212 PRINT_INDENT(level);
213 if (tlv->len > 0) {
214 fprintf(f, "\tvalue: %s\n", tlv->value[0]?"true":"false");
215 } else {
216 fprintf(f, "n/a\n");
217 }
218}
219
220static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level) {
221 PRINT_INDENT(level);
222 if (tlv->len == 4) {
223 int32_t val = 0;
224 for (int i = 0; i < tlv->len; i++)
225 val = (val << 8) + tlv->value[i];
226 fprintf(f, "\tvalue4b: %d\n", val);
227 return;
228 }
229 fprintf(f, "\tvalue: %lu\n", asn1_value_integer(tlv, 0, tlv->len * 2));
230}
231
232static char *asn1_oid_description(const char *oid, bool with_group_desc) {
233 json_error_t error;
234 json_t *root = NULL;
235 char fname[300] = {0};
236 static char res[300];
237 memset(res, 0x00, sizeof(res));
238
239 strcpy(fname, get_my_executable_directory());
240 strcat(fname, "crypto/oids.json");
241 if (access(fname, F_OK) < 0) {
242 strcpy(fname, get_my_executable_directory());
243 strcat(fname, "oids.json");
244 if (access(fname, F_OK) < 0) {
245 goto error; // file not found
246 }
247 }
248
249 // load `oids.json`
250 root = json_load_file(fname, 0, &error);
251
252 if (!root || !json_is_object(root)) {
253 goto error;
254 }
255
256 json_t *elm = json_object_get(root, oid);
257 if (!elm) {
258 goto error;
259 }
260
261 if (JsonLoadStr(elm, "$.d", res))
262 goto error;
263
264 char strext[300] = {0};
265 if (!JsonLoadStr(elm, "$.c", strext)) {
266 strcat(res, " (");
267 strcat(res, strext);
268 strcat(res, ")");
269 }
270
271 json_decref(root);
272 return res;
273
274error:
275 if (root)
276 json_decref(root);
277 return NULL;
278}
279
280static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag *tag, FILE *f, int level) {
281 PRINT_INDENT(level);
282 mbedtls_asn1_buf asn1_buf;
283 asn1_buf.len = tlv->len;
284 asn1_buf.p = (uint8_t *)tlv->value;
285 char pstr[300];
286 mbedtls_oid_get_numeric_string(pstr, sizeof(pstr), &asn1_buf);
287 fprintf(f, " %s", pstr);
288
289 char *jsondesc = asn1_oid_description(pstr, true);
290 if (jsondesc) {
291 fprintf(f, " - %s", jsondesc);
292 } else {
293 const char *ppstr;
294 mbedtls_oid_get_attr_short_name(&asn1_buf, &ppstr);
295 if (ppstr && strnlen(ppstr, 1)) {
296 fprintf(f, " (%s)\n", ppstr);
297 return;
298 }
299 mbedtls_oid_get_sig_alg_desc(&asn1_buf, &ppstr);
300 if (ppstr && strnlen(ppstr, 1)) {
301 fprintf(f, " (%s)\n", ppstr);
302 return;
303 }
304 mbedtls_oid_get_extended_key_usage(&asn1_buf, &ppstr);
305 if (ppstr && strnlen(ppstr, 1)) {
306 fprintf(f, " (%s)\n", ppstr);
307 return;
308 }
309 }
310 fprintf(f, "\n");
311}
312
313bool asn1_tag_dump(const struct tlv *tlv, FILE *f, int level, bool *candump) {
314 if (!tlv) {
315 fprintf(f, "NULL\n");
316 return false;
317 }
318
319 const struct asn1_tag *tag = asn1_get_tag(tlv);
320
321 PRINT_INDENT(level);
322 fprintf(f, "--%2hx[%02zx] '%s':", tlv->tag, tlv->len, tag->name);
323
324 switch (tag->type) {
325 case ASN1_TAG_GENERIC:
326 fprintf(f, "\n");
327 break;
328 case ASN1_TAG_STRING:
329 asn1_tag_dump_string(tlv, tag, f, level);
330 *candump = false;
331 break;
332 case ASN1_TAG_OCTET_STRING:
333 asn1_tag_dump_octet_string(tlv, tag, f, level, candump);
334 break;
335 case ASN1_TAG_BOOLEAN:
336 asn1_tag_dump_boolean(tlv, tag, f, level);
337 *candump = false;
338 break;
339 case ASN1_TAG_INTEGER:
340 asn1_tag_dump_integer(tlv, tag, f, level);
341 *candump = false;
342 break;
343 case ASN1_TAG_UTC_TIME:
344 asn1_tag_dump_str_time(tlv, tag, f, level, false, candump);
345 break;
346 case ASN1_TAG_STR_TIME:
347 asn1_tag_dump_str_time(tlv, tag, f, level, true, candump);
348 break;
349 case ASN1_TAG_OBJECT_ID:
350 asn1_tag_dump_object_id(tlv, tag, f, level);
351 *candump = false;
352 break;
353 };
354
355 return true;
356}
Impressum, Datenschutz