X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/27d06e044795c0b0ea4d34b10bccfa41d57f77fc..0bb514502a1d80e9b023d0e8a379f3559798eec2:/client/fido/cbortools.c diff --git a/client/fido/cbortools.c b/client/fido/cbortools.c new file mode 100644 index 00000000..01691dad --- /dev/null +++ b/client/fido/cbortools.c @@ -0,0 +1,491 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Tools for work with CBOR format http://cbor.io/spec.html +// via Intel tinycbor (https://github.com/intel/tinycbor) library +//----------------------------------------------------------------------------- +// + +#include "cbortools.h" +#include +#include "emv/emvjson.h" +#include "util.h" +#include "fidocore.h" + +static void indent(int nestingLevel) { + while (nestingLevel--) + printf(" "); +} + +static CborError dumpelm(CborValue *it, bool *got_next, int nestingLevel) { + CborError err; + *got_next = false; + + CborType type = cbor_value_get_type(it); + indent(nestingLevel); + switch (type) { + case CborMapType: + case CborArrayType: { + printf(type == CborArrayType ? "Array[" : "Map["); + break; + } + + case CborIntegerType: { + int64_t val; + cbor_value_get_int64(it, &val); // can't fail + printf("%lld", (long long)val); + break; + } + + case CborByteStringType: { + uint8_t *buf; + size_t n; + err = cbor_value_dup_byte_string(it, &buf, &n, it); + *got_next = true; + if (err) + return err; // parse error + printf("%s", sprint_hex(buf, n)); + free(buf); + break; + } + + case CborTextStringType: { + char *buf; + size_t n; + err = cbor_value_dup_text_string(it, &buf, &n, it); + *got_next = true; + if (err) + return err; // parse error + printf("%s", buf); + free(buf); + break; + } + + case CborTagType: { + CborTag tag; + cbor_value_get_tag(it, &tag); + printf("Tag(%lld)", (long long)tag); + break; + } + + case CborSimpleType: { + uint8_t type; + cbor_value_get_simple_type(it, &type); + printf("simple(%u)", type); + break; + } + + case CborNullType: + printf("null"); + break; + + case CborUndefinedType: + printf("undefined"); + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); // can't fail + printf("%s", val ? "true" : "false"); + break; + } + + case CborDoubleType: { + double val; + if (false) { + float f; + case CborFloatType: + cbor_value_get_float(it, &f); + val = f; + } else { + cbor_value_get_double(it, &val); + } + printf("%g", val); + break; + } + case CborHalfFloatType: { + uint16_t val; + cbor_value_get_half_float(it, &val); + printf("__f16(%04x)", val); + break; + } + + case CborInvalidType: + printf("CborInvalidType!!!"); + break; + } + + return CborNoError; +} + +static CborError dumprecursive(uint8_t cmdCode, bool isResponse, CborValue *it, bool isMapType, int nestingLevel) { + int elmCount = 0; + while (!cbor_value_at_end(it)) { + CborError err; + CborType type = cbor_value_get_type(it); +//printf("^%x^", type); + bool got_next; + + switch (type) { + case CborMapType: + case CborArrayType: { + // recursive type + CborValue recursed; + assert(cbor_value_is_container(it)); + if (!(isMapType && (elmCount % 2))) + indent(nestingLevel); + printf(type == CborArrayType ? "Array[\n" : "Map[\n"); + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; // parse error + err = dumprecursive(cmdCode, isResponse, &recursed, (type == CborMapType), nestingLevel + 1); + if (err) + return err; // parse error + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; // parse error + indent(nestingLevel); + printf("]"); + got_next = true; + break; + } + + default: { + err = dumpelm(it, &got_next, (isMapType && (elmCount % 2)) ? 0 : nestingLevel); + if (err) + return err; + if (cmdCode > 0 && nestingLevel == 1 && isMapType && !(elmCount % 2)) { + int64_t val; + cbor_value_get_int64(it, &val); + char *desc = fido2GetCmdMemberDescription(cmdCode, isResponse, val); + if (desc) + printf(" (%s)", desc); + } + break; + } + } + + if (!got_next) { + err = cbor_value_advance_fixed(it); + if (err) + return err; + } + if (isMapType && !(elmCount % 2)) { + printf(": "); + } else { + printf("\n"); + } + elmCount++; + } + return CborNoError; +} + +int TinyCborInit(uint8_t *data, size_t length, CborValue *cb) { + CborParser parser; + CborError err = cbor_parser_init(data, length, 0, &parser, cb); + if (err) + return err; + + return 0; +} + +int TinyCborPrintFIDOPackage(uint8_t cmdCode, bool isResponse, uint8_t *data, size_t length) { + CborValue cb; + int res; + res = TinyCborInit(data, length, &cb); + if (res) + return res; + + CborError err = dumprecursive(cmdCode, isResponse, &cb, false, 0); + + if (err) { + fprintf(stderr, +#if __WORDSIZE == 64 + "CBOR parsing failure at offset %" PRId64 " : %s\n", +#else + "CBOR parsing failure at offset %" PRId32 " : %s\n", +#endif + cb.ptr - data, cbor_error_string(err)); + return 1; + } + + return 0; +} + +int JsonObjElmCount(json_t *elm) { + int res = 0; + const char *key; + json_t *value; + + if (!json_is_object(elm)) + return 0; + + json_object_foreach(elm, key, value) { + if (strlen(key) > 0 && key[0] != '.') + res++; + } + + return res; +} + +int JsonToCbor(json_t *elm, CborEncoder *encoder) { + if (!elm || !encoder) + return 1; + + int res; + + // CBOR map == JSON object + if (json_is_object(elm)) { + CborEncoder map; + const char *key; + json_t *value; + + res = cbor_encoder_create_map(encoder, &map, JsonObjElmCount(elm)); + cbor_check(res); + + json_object_foreach(elm, key, value) { + if (strlen(key) > 0 && key[0] != '.') { + res = cbor_encode_text_stringz(&map, key); + cbor_check(res); + + // RECURSION! + JsonToCbor(value, &map); + } + } + + res = cbor_encoder_close_container(encoder, &map); + cbor_check(res); + } + + // CBOR array == JSON array + if (json_is_array(elm)) { + size_t index; + json_t *value; + CborEncoder array; + + res = cbor_encoder_create_array(encoder, &array, json_array_size(elm)); + cbor_check(res); + + json_array_foreach(elm, index, value) { + // RECURSION! + JsonToCbor(value, &array); + } + + res = cbor_encoder_close_container(encoder, &array); + cbor_check(res); + } + + if (json_is_boolean(elm)) { + res = cbor_encode_boolean(encoder, json_is_true(elm)); + cbor_check(res); + } + + if (json_is_integer(elm)) { + res = cbor_encode_int(encoder, json_integer_value(elm)); + cbor_check(res); + } + + if (json_is_real(elm)) { + res = cbor_encode_float(encoder, json_real_value(elm)); + cbor_check(res); + } + + if (json_is_string(elm)) { + const char * val = json_string_value(elm); + if (CheckStringIsHEXValue(val)) { + size_t datalen = 0; + uint8_t data[4096] = {0}; + res = JsonLoadBufAsHex(elm, "$", data, sizeof(data), &datalen); + if (res) + return 100; + + res = cbor_encode_byte_string(encoder, data, datalen); + cbor_check(res); + } else { + res = cbor_encode_text_stringz(encoder, val); + cbor_check(res); + } + } + + + + return 0; +} + +int CborMapGetKeyById(CborParser *parser, CborValue *map, uint8_t *data, size_t dataLen, int key) { + CborValue cb; + + CborError err = cbor_parser_init(data, dataLen, 0, parser, &cb); + cbor_check(err); + + if (cbor_value_get_type(&cb) != CborMapType) + return 1; + + err = cbor_value_enter_container(&cb, map); + cbor_check(err); + + int64_t indx; + while (!cbor_value_at_end(map)) { + // check number + if (cbor_value_get_type(map) != CborIntegerType) + return 1; + + cbor_value_get_int64(map, &indx); + + err = cbor_value_advance(map); + cbor_check(err); + + if (indx == key) + return 0; + + // pass value + err = cbor_value_advance(map); + cbor_check(err); + } + + err = cbor_value_leave_container(&cb, map); + cbor_check(err); + + return 2; +} + +CborError CborGetArrayBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen) { + return CborGetArrayBinStringValueEx(elm, data, maxdatalen, datalen, NULL, 0); +} + +CborError CborGetArrayBinStringValueEx(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen, uint8_t *delimeter, size_t delimeterlen) { + CborValue array; + if (datalen) + *datalen = 0; + + size_t slen = maxdatalen; + size_t totallen = 0; + + CborError res = cbor_value_enter_container(elm, &array); + cbor_check(res); + + while (!cbor_value_at_end(&array)) { + res = cbor_value_copy_byte_string(&array, &data[totallen], &slen, &array); + cbor_check(res); + + totallen += slen; + if (delimeter) { + memcpy(&data[totallen], delimeter, delimeterlen); + totallen += delimeterlen; + } + slen = maxdatalen - totallen; + } + + res = cbor_value_leave_container(elm, &array); + cbor_check(res); + + if (datalen) + *datalen = totallen; + + return CborNoError; +}; + +CborError CborGetBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen) { + if (datalen) + *datalen = 0; + + size_t slen = maxdatalen; + + CborError res = cbor_value_copy_byte_string(elm, data, &slen, elm); + cbor_check(res); + + if (datalen) + *datalen = slen; + + return CborNoError; +}; + +CborError CborGetArrayStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen, char *delimeter) { + CborValue array; + if (datalen) + *datalen = 0; + + size_t slen = maxdatalen; + size_t totallen = 0; + + CborError res = cbor_value_enter_container(elm, &array); + cbor_check(res); + + while (!cbor_value_at_end(&array)) { + res = cbor_value_copy_text_string(&array, &data[totallen], &slen, &array); + cbor_check(res); + + totallen += slen; + if (delimeter) { + strcat(data, delimeter); + totallen += strlen(delimeter); + } + slen = maxdatalen - totallen; + data[totallen] = 0x00; + } + + res = cbor_value_leave_container(elm, &array); + cbor_check(res); + + if (datalen) + *datalen = totallen; + + return CborNoError; +}; + +CborError CborGetStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen) { + if (datalen) + *datalen = 0; + + size_t slen = maxdatalen; + + CborError res = cbor_value_copy_text_string(elm, data, &slen, elm); + cbor_check(res); + + if (datalen) + *datalen = slen; + + return CborNoError; +}; + +CborError CborGetStringValueBuf(CborValue *elm) { + static char stringBuf[2048]; + memset(stringBuf, 0x00, sizeof(stringBuf)); + + return CborGetStringValue(elm, stringBuf, sizeof(stringBuf), NULL); +}; + +int CBOREncodeElm(json_t *root, char *rootElmId, CborEncoder *encoder) { + json_t *elm = NULL; + if (rootElmId && strlen(rootElmId) && rootElmId[0] == '$') + elm = json_path_get(root, rootElmId); + else + elm = json_object_get(root, rootElmId); + + if (!elm) + return 1; + + int res = JsonToCbor(elm, encoder); + + return res; +} + +CborError CBOREncodeClientDataHash(json_t *root, CborEncoder *encoder) { + uint8_t buf[100] = {0}; + size_t jlen; + + JsonLoadBufAsHex(root, "$.ClientDataHash", buf, sizeof(buf), &jlen); + + // fill with 0x00 if not found + if (!jlen) + jlen = 32; + + int res = cbor_encode_byte_string(encoder, buf, jlen); + cbor_check(res); + + return 0; +}