1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
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
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"
15 #include "emv/emvjson.h"
17 #include "ui.h" // PrintAndLogEx(
20 static void indent(int nestingLevel
) {
21 while (nestingLevel
--)
22 PrintAndLogEx(NORMAL
, " " NOLF
);
25 static CborError
dumpelm(CborValue
*it
, bool *got_next
, int nestingLevel
) {
29 CborType type
= cbor_value_get_type(it
);
34 PrintAndLogEx(NORMAL
, "%s" NOLF
, (type
== CborArrayType
) ? "Array[" : "Map[");
38 case CborIntegerType
: {
40 cbor_value_get_int64(it
, &val
); // can't fail
41 PrintAndLogEx(NORMAL
, "%lld" NOLF
, (long long)val
);
45 case CborByteStringType
: {
48 err
= cbor_value_dup_byte_string(it
, &buf
, &n
, it
);
51 return err
; // parse error
53 PrintAndLogEx(NORMAL
, "%s" NOLF
, sprint_hex(buf
, n
));
58 case CborTextStringType
: {
61 err
= cbor_value_dup_text_string(it
, &buf
, &n
, it
);
64 return err
; // parse error
66 PrintAndLogEx(NORMAL
, "%s" NOLF
, buf
);
73 cbor_value_get_tag(it
, &tag
);
74 PrintAndLogEx(NORMAL
, "Tag(%lld)" NOLF
, (long long)tag
);
78 case CborSimpleType
: {
80 cbor_value_get_simple_type(it
, &t
);
81 PrintAndLogEx(NORMAL
, "simple(%u)" NOLF
, t
);
86 PrintAndLogEx(NORMAL
, "null" NOLF
);
89 case CborUndefinedType
:
90 PrintAndLogEx(NORMAL
, "undefined" NOLF
);
93 case CborBooleanType
: {
95 cbor_value_get_boolean(it
, &val
); // can't fail
96 PrintAndLogEx(NORMAL
, "%s" NOLF
, (val
) ? "true" : "false");
100 case CborDoubleType
: {
105 cbor_value_get_float(it
, &f
);
108 cbor_value_get_double(it
, &val
);
110 PrintAndLogEx(NORMAL
, "%g" NOLF
, val
);
113 case CborHalfFloatType
: {
115 cbor_value_get_half_float(it
, &val
);
116 PrintAndLogEx(NORMAL
, "__f16(%04x)" NOLF
, val
);
120 case CborInvalidType
:
121 PrintAndLogEx(NORMAL
, _RED_("CborInvalidType!!!") NOLF
);
128 static CborError
dumprecursive(uint8_t cmdCode
, bool isResponse
, CborValue
*it
, bool isMapType
, int nestingLevel
) {
130 while (!cbor_value_at_end(it
)) {
132 CborType type
= cbor_value_get_type(it
);
134 bool got_next
= false;
138 case CborArrayType
: {
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
);
149 return err
; // parse error
151 err
= dumprecursive(cmdCode
, isResponse
, &recursed
, (type
== CborMapType
), nestingLevel
+ 1);
153 return err
; // parse error
155 err
= cbor_value_leave_container(it
, &recursed
);
157 return err
; // parse error
159 indent(nestingLevel
);
160 PrintAndLogEx(NORMAL
, "]" NOLF
);
164 case CborByteStringType
:
165 case CborTextStringType
:
168 case CborBooleanType
:
170 case CborUndefinedType
:
171 case CborHalfFloatType
:
174 case CborInvalidType
:
175 case CborIntegerType
: {
176 err
= dumpelm(it
, &got_next
, (isMapType
&& (elmCount
% 2)) ? 0 : nestingLevel
);
180 if (cmdCode
> 0 && nestingLevel
== 1 && isMapType
&& !(elmCount
% 2)) {
182 cbor_value_get_int64(it
, &val
);
183 const char *desc
= fido2GetCmdMemberDescription(cmdCode
, isResponse
, val
);
185 PrintAndLogEx(NORMAL
, " (%s)" NOLF
, desc
);
192 err
= cbor_value_advance_fixed(it
);
196 if (isMapType
&& !(elmCount
% 2)) {
197 PrintAndLogEx(NORMAL
, ": " NOLF
);
199 PrintAndLogEx(NORMAL
, "");
206 static int TinyCborInit(uint8_t *data
, size_t length
, CborValue
*cb
) {
208 CborError err
= cbor_parser_init(data
, length
, 0, &parser
, cb
);
215 int TinyCborPrintFIDOPackage(uint8_t cmdCode
, bool isResponse
, uint8_t *data
, size_t length
) {
218 res
= TinyCborInit(data
, length
, &cb
);
222 CborError err
= dumprecursive(cmdCode
, isResponse
, &cb
, false, 0);
226 "CBOR parsing failure at offset %" PRIu32
" : %s\n",
227 (uint32_t)(cb
.ptr
- data
),
228 cbor_error_string(err
)
236 static int JsonObjElmCount(json_t
*elm
) {
241 if (!json_is_object(elm
))
244 json_object_foreach(elm
, key
, value
) {
245 if (strlen(key
) > 0 && key
[0] != '.')
252 int JsonToCbor(json_t
*elm
, CborEncoder
*encoder
) {
253 if (!elm
|| !encoder
)
258 // CBOR map == JSON object
259 if (json_is_object(elm
)) {
264 res
= cbor_encoder_create_map(encoder
, &map
, JsonObjElmCount(elm
));
267 json_object_foreach(elm
, key
, value
) {
268 if (strlen(key
) > 0 && key
[0] != '.') {
269 res
= cbor_encode_text_stringz(&map
, key
);
273 JsonToCbor(value
, &map
);
277 res
= cbor_encoder_close_container(encoder
, &map
);
281 // CBOR array == JSON array
282 if (json_is_array(elm
)) {
287 res
= cbor_encoder_create_array(encoder
, &array
, json_array_size(elm
));
290 json_array_foreach(elm
, index
, value
) {
292 JsonToCbor(value
, &array
);
295 res
= cbor_encoder_close_container(encoder
, &array
);
299 if (json_is_boolean(elm
)) {
300 res
= cbor_encode_boolean(encoder
, json_is_true(elm
));
304 if (json_is_integer(elm
)) {
305 res
= cbor_encode_int(encoder
, json_integer_value(elm
));
309 if (json_is_real(elm
)) {
310 res
= cbor_encode_float(encoder
, json_real_value(elm
));
314 if (json_is_string(elm
)) {
315 const char *val
= json_string_value(elm
);
316 if (CheckStringIsHEXValue(val
)) {
318 uint8_t data
[4096] = {0};
319 res
= JsonLoadBufAsHex(elm
, "$", data
, sizeof(data
), &datalen
);
323 res
= cbor_encode_byte_string(encoder
, data
, datalen
);
326 res
= cbor_encode_text_stringz(encoder
, val
);
336 int CborMapGetKeyById(CborParser
*parser
, CborValue
*map
, uint8_t *data
, size_t dataLen
, int key
) {
339 CborError err
= cbor_parser_init(data
, dataLen
, 0, parser
, &cb
);
342 if (cbor_value_get_type(&cb
) != CborMapType
)
345 err
= cbor_value_enter_container(&cb
, map
);
349 while (!cbor_value_at_end(map
)) {
351 if (cbor_value_get_type(map
) != CborIntegerType
)
354 cbor_value_get_int64(map
, &indx
);
356 err
= cbor_value_advance(map
);
363 err
= cbor_value_advance(map
);
367 err
= cbor_value_leave_container(&cb
, map
);
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
) {
382 size_t slen
= maxdatalen
;
385 CborError res
= cbor_value_enter_container(elm
, &array
);
388 while (!cbor_value_at_end(&array
)) {
389 res
= cbor_value_copy_byte_string(&array
, &data
[totallen
], &slen
, &array
);
394 memcpy(&data
[totallen
], delimiter
, delimiterlen
);
395 totallen
+= delimiterlen
;
397 slen
= maxdatalen
- totallen
;
400 res
= cbor_value_leave_container(elm
, &array
);
409 CborError
CborGetBinStringValue(CborValue
*elm
, uint8_t *data
, size_t maxdatalen
, size_t *datalen
) {
413 size_t slen
= maxdatalen
;
415 CborError res
= cbor_value_copy_byte_string(elm
, data
, &slen
, elm
);
424 CborError
CborGetArrayStringValue(CborValue
*elm
, char *data
, size_t maxdatalen
, size_t *datalen
, char *delimiter
) {
429 size_t slen
= maxdatalen
;
432 CborError res
= cbor_value_enter_container(elm
, &array
);
435 while (!cbor_value_at_end(&array
)) {
436 res
= cbor_value_copy_text_string(&array
, &data
[totallen
], &slen
, &array
);
441 strcat(data
, delimiter
);
442 totallen
+= strlen(delimiter
);
444 slen
= maxdatalen
- totallen
;
445 data
[totallen
] = 0x00;
448 res
= cbor_value_leave_container(elm
, &array
);
457 CborError
CborGetStringValue(CborValue
*elm
, char *data
, size_t maxdatalen
, size_t *datalen
) {
461 size_t slen
= maxdatalen
;
463 CborError res
= cbor_value_copy_text_string(elm
, data
, &slen
, elm
);
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
) {
481 if (rootElmId
&& strlen(rootElmId
) && rootElmId
[0] == '$')
482 elm
= json_path_get(root
, rootElmId
);
484 elm
= json_object_get(root
, rootElmId
);
489 int res
= JsonToCbor(elm
, encoder
);
494 CborError
CBOREncodeClientDataHash(json_t
*root
, CborEncoder
*encoder
) {
495 uint8_t buf
[100] = {0};
498 JsonLoadBufAsHex(root
, "$.ClientDataHash", buf
, sizeof(buf
), &jlen
);
500 // fill with 0x00 if not found
504 int res
= cbor_encode_byte_string(encoder
, buf
, jlen
);