textual
[RRG-proxmark3.git] / client / src / fido / cbortools.c
blob7a7a1d25e76bd915a9866381bc9a79143d7c192e
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 // Tools for work with CBOR format http://cbor.io/spec.html
9 // via Intel tinycbor (https://github.com/intel/tinycbor) library
10 //-----------------------------------------------------------------------------
13 #include "cbortools.h"
14 #include <inttypes.h>
15 #include "emv/emvjson.h"
16 #include "util.h"
17 #include "ui.h" // PrintAndLogEx(
18 #include "fidocore.h"
20 static void indent(int nestingLevel) {
21 while (nestingLevel--)
22 PrintAndLogEx(NORMAL, " " NOLF);
25 static CborError dumpelm(CborValue *it, bool *got_next, int nestingLevel) {
26 CborError err;
27 *got_next = false;
29 CborType type = cbor_value_get_type(it);
30 indent(nestingLevel);
31 switch (type) {
32 case CborMapType:
33 case CborArrayType: {
34 PrintAndLogEx(NORMAL, "%s" NOLF, (type == CborArrayType) ? "Array[" : "Map[");
35 break;
38 case CborIntegerType: {
39 int64_t val;
40 cbor_value_get_int64(it, &val); // can't fail
41 PrintAndLogEx(NORMAL, "%lld" NOLF, (long long)val);
42 break;
45 case CborByteStringType: {
46 uint8_t *buf;
47 size_t n;
48 err = cbor_value_dup_byte_string(it, &buf, &n, it);
49 *got_next = true;
50 if (err)
51 return err; // parse error
53 PrintAndLogEx(NORMAL, "%s" NOLF, sprint_hex(buf, n));
54 free(buf);
55 break;
58 case CborTextStringType: {
59 char *buf;
60 size_t n;
61 err = cbor_value_dup_text_string(it, &buf, &n, it);
62 *got_next = true;
63 if (err)
64 return err; // parse error
66 PrintAndLogEx(NORMAL, "%s" NOLF, buf);
67 free(buf);
68 break;
71 case CborTagType: {
72 CborTag tag;
73 cbor_value_get_tag(it, &tag);
74 PrintAndLogEx(NORMAL, "Tag(%lld)" NOLF, (long long)tag);
75 break;
78 case CborSimpleType: {
79 uint8_t t;
80 cbor_value_get_simple_type(it, &t);
81 PrintAndLogEx(NORMAL, "simple(%u)" NOLF, t);
82 break;
85 case CborNullType:
86 PrintAndLogEx(NORMAL, "null" NOLF);
87 break;
89 case CborUndefinedType:
90 PrintAndLogEx(NORMAL, "undefined" NOLF);
91 break;
93 case CborBooleanType: {
94 bool val;
95 cbor_value_get_boolean(it, &val); // can't fail
96 PrintAndLogEx(NORMAL, "%s" NOLF, (val) ? "true" : "false");
97 break;
100 case CborDoubleType: {
101 double val;
102 if (false) {
103 float f;
104 case CborFloatType:
105 cbor_value_get_float(it, &f);
106 val = f;
107 } else {
108 cbor_value_get_double(it, &val);
110 PrintAndLogEx(NORMAL, "%g" NOLF, val);
111 break;
113 case CborHalfFloatType: {
114 uint16_t val;
115 cbor_value_get_half_float(it, &val);
116 PrintAndLogEx(NORMAL, "__f16(%04x)" NOLF, val);
117 break;
120 case CborInvalidType:
121 PrintAndLogEx(NORMAL, _RED_("CborInvalidType!!!") NOLF);
122 break;
125 return CborNoError;
128 static CborError dumprecursive(uint8_t cmdCode, bool isResponse, CborValue *it, bool isMapType, int nestingLevel) {
129 int elmCount = 0;
130 while (!cbor_value_at_end(it)) {
131 CborError err;
132 CborType type = cbor_value_get_type(it);
134 bool got_next = false;
136 switch (type) {
137 case CborMapType:
138 case CborArrayType: {
139 // recursive type
140 CborValue recursed;
141 assert(cbor_value_is_container(it));
142 if (!(isMapType && (elmCount % 2)))
143 indent(nestingLevel);
145 PrintAndLogEx(NORMAL, "%s" NOLF, (type == CborArrayType) ? "Array[\n" : "Map[\n");
147 err = cbor_value_enter_container(it, &recursed);
148 if (err)
149 return err; // parse error
151 err = dumprecursive(cmdCode, isResponse, &recursed, (type == CborMapType), nestingLevel + 1);
152 if (err)
153 return err; // parse error
155 err = cbor_value_leave_container(it, &recursed);
156 if (err)
157 return err; // parse error
159 indent(nestingLevel);
160 PrintAndLogEx(NORMAL, "]" NOLF);
161 got_next = true;
162 break;
164 case CborByteStringType:
165 case CborTextStringType:
166 case CborTagType:
167 case CborSimpleType:
168 case CborBooleanType:
169 case CborNullType:
170 case CborUndefinedType:
171 case CborHalfFloatType:
172 case CborFloatType:
173 case CborDoubleType:
174 case CborInvalidType:
175 case CborIntegerType: {
176 err = dumpelm(it, &got_next, (isMapType && (elmCount % 2)) ? 0 : nestingLevel);
177 if (err)
178 return err;
180 if (cmdCode > 0 && nestingLevel == 1 && isMapType && !(elmCount % 2)) {
181 int64_t val;
182 cbor_value_get_int64(it, &val);
183 const char *desc = fido2GetCmdMemberDescription(cmdCode, isResponse, val);
184 if (desc)
185 PrintAndLogEx(NORMAL, " (%s)" NOLF, desc);
187 break;
191 if (!got_next) {
192 err = cbor_value_advance_fixed(it);
193 if (err)
194 return err;
196 if (isMapType && !(elmCount % 2)) {
197 PrintAndLogEx(NORMAL, ": " NOLF);
198 } else {
199 PrintAndLogEx(NORMAL, "");
201 elmCount++;
203 return CborNoError;
206 static int TinyCborInit(uint8_t *data, size_t length, CborValue *cb) {
207 CborParser parser;
208 CborError err = cbor_parser_init(data, length, 0, &parser, cb);
209 if (err)
210 return err;
212 return 0;
215 int TinyCborPrintFIDOPackage(uint8_t cmdCode, bool isResponse, uint8_t *data, size_t length) {
216 CborValue cb;
217 int res;
218 res = TinyCborInit(data, length, &cb);
219 if (res)
220 return res;
222 CborError err = dumprecursive(cmdCode, isResponse, &cb, false, 0);
224 if (err) {
225 fprintf(stderr,
226 "CBOR parsing failure at offset %" PRIu32 " : %s\n",
227 (uint32_t)(cb.ptr - data),
228 cbor_error_string(err)
230 return 1;
233 return 0;
236 static int JsonObjElmCount(json_t *elm) {
237 int res = 0;
238 const char *key;
239 json_t *value;
241 if (!json_is_object(elm))
242 return 0;
244 json_object_foreach(elm, key, value) {
245 if (strlen(key) > 0 && key[0] != '.')
246 res++;
249 return res;
252 int JsonToCbor(json_t *elm, CborEncoder *encoder) {
253 if (!elm || !encoder)
254 return 1;
256 int res;
258 // CBOR map == JSON object
259 if (json_is_object(elm)) {
260 CborEncoder map;
261 const char *key;
262 json_t *value;
264 res = cbor_encoder_create_map(encoder, &map, JsonObjElmCount(elm));
265 cbor_check(res);
267 json_object_foreach(elm, key, value) {
268 if (strlen(key) > 0 && key[0] != '.') {
269 res = cbor_encode_text_stringz(&map, key);
270 cbor_check(res);
272 // RECURSION!
273 JsonToCbor(value, &map);
277 res = cbor_encoder_close_container(encoder, &map);
278 cbor_check(res);
281 // CBOR array == JSON array
282 if (json_is_array(elm)) {
283 size_t index;
284 json_t *value;
285 CborEncoder array;
287 res = cbor_encoder_create_array(encoder, &array, json_array_size(elm));
288 cbor_check(res);
290 json_array_foreach(elm, index, value) {
291 // RECURSION!
292 JsonToCbor(value, &array);
295 res = cbor_encoder_close_container(encoder, &array);
296 cbor_check(res);
299 if (json_is_boolean(elm)) {
300 res = cbor_encode_boolean(encoder, json_is_true(elm));
301 cbor_check(res);
304 if (json_is_integer(elm)) {
305 res = cbor_encode_int(encoder, json_integer_value(elm));
306 cbor_check(res);
309 if (json_is_real(elm)) {
310 res = cbor_encode_float(encoder, json_real_value(elm));
311 cbor_check(res);
314 if (json_is_string(elm)) {
315 const char *val = json_string_value(elm);
316 if (CheckStringIsHEXValue(val)) {
317 size_t datalen = 0;
318 uint8_t data[4096] = {0};
319 res = JsonLoadBufAsHex(elm, "$", data, sizeof(data), &datalen);
320 if (res)
321 return 100;
323 res = cbor_encode_byte_string(encoder, data, datalen);
324 cbor_check(res);
325 } else {
326 res = cbor_encode_text_stringz(encoder, val);
327 cbor_check(res);
333 return 0;
336 int CborMapGetKeyById(CborParser *parser, CborValue *map, uint8_t *data, size_t dataLen, int key) {
337 CborValue cb;
339 CborError err = cbor_parser_init(data, dataLen, 0, parser, &cb);
340 cbor_check(err);
342 if (cbor_value_get_type(&cb) != CborMapType)
343 return 1;
345 err = cbor_value_enter_container(&cb, map);
346 cbor_check(err);
348 int64_t indx;
349 while (!cbor_value_at_end(map)) {
350 // check number
351 if (cbor_value_get_type(map) != CborIntegerType)
352 return 1;
354 cbor_value_get_int64(map, &indx);
356 err = cbor_value_advance(map);
357 cbor_check(err);
359 if (indx == key)
360 return 0;
362 // pass value
363 err = cbor_value_advance(map);
364 cbor_check(err);
367 err = cbor_value_leave_container(&cb, map);
368 cbor_check(err);
370 return 2;
373 CborError CborGetArrayBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen) {
374 return CborGetArrayBinStringValueEx(elm, data, maxdatalen, datalen, NULL, 0);
377 CborError CborGetArrayBinStringValueEx(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen, uint8_t *delimiter, size_t delimiterlen) {
378 CborValue array;
379 if (datalen)
380 *datalen = 0;
382 size_t slen = maxdatalen;
383 size_t totallen = 0;
385 CborError res = cbor_value_enter_container(elm, &array);
386 cbor_check(res);
388 while (!cbor_value_at_end(&array)) {
389 res = cbor_value_copy_byte_string(&array, &data[totallen], &slen, &array);
390 cbor_check(res);
392 totallen += slen;
393 if (delimiter) {
394 memcpy(&data[totallen], delimiter, delimiterlen);
395 totallen += delimiterlen;
397 slen = maxdatalen - totallen;
400 res = cbor_value_leave_container(elm, &array);
401 cbor_check(res);
403 if (datalen)
404 *datalen = totallen;
406 return CborNoError;
409 CborError CborGetBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen) {
410 if (datalen)
411 *datalen = 0;
413 size_t slen = maxdatalen;
415 CborError res = cbor_value_copy_byte_string(elm, data, &slen, elm);
416 cbor_check(res);
418 if (datalen)
419 *datalen = slen;
421 return CborNoError;
424 CborError CborGetArrayStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen, char *delimiter) {
425 CborValue array;
426 if (datalen)
427 *datalen = 0;
429 size_t slen = maxdatalen;
430 size_t totallen = 0;
432 CborError res = cbor_value_enter_container(elm, &array);
433 cbor_check(res);
435 while (!cbor_value_at_end(&array)) {
436 res = cbor_value_copy_text_string(&array, &data[totallen], &slen, &array);
437 cbor_check(res);
439 totallen += slen;
440 if (delimiter) {
441 strcat(data, delimiter);
442 totallen += strlen(delimiter);
444 slen = maxdatalen - totallen;
445 data[totallen] = 0x00;
448 res = cbor_value_leave_container(elm, &array);
449 cbor_check(res);
451 if (datalen)
452 *datalen = totallen;
454 return CborNoError;
457 CborError CborGetStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen) {
458 if (datalen)
459 *datalen = 0;
461 size_t slen = maxdatalen;
463 CborError res = cbor_value_copy_text_string(elm, data, &slen, elm);
464 cbor_check(res);
466 if (datalen)
467 *datalen = slen;
469 return CborNoError;
472 CborError CborGetStringValueBuf(CborValue *elm) {
473 static char stringBuf[2048];
474 memset(stringBuf, 0x00, sizeof(stringBuf));
476 return CborGetStringValue(elm, stringBuf, sizeof(stringBuf), NULL);
479 int CBOREncodeElm(json_t *root, const char *rootElmId, CborEncoder *encoder) {
480 json_t *elm = NULL;
481 if (rootElmId && strlen(rootElmId) && rootElmId[0] == '$')
482 elm = json_path_get(root, rootElmId);
483 else
484 elm = json_object_get(root, rootElmId);
486 if (!elm)
487 return 1;
489 int res = JsonToCbor(elm, encoder);
491 return res;
494 CborError CBOREncodeClientDataHash(json_t *root, CborEncoder *encoder) {
495 uint8_t buf[100] = {0};
496 size_t jlen;
498 JsonLoadBufAsHex(root, "$.ClientDataHash", buf, sizeof(buf), &jlen);
500 // fill with 0x00 if not found
501 if (!jlen)
502 jlen = 32;
504 int res = cbor_encode_byte_string(encoder, buf, jlen);
505 cbor_check(res);
507 return 0;