diff --git a/client/Makefile b/client/Makefile index 90b16830..931df81f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -138,6 +138,7 @@ CMDSRCS = $(SRC_SMARTCARD) \ emv/tlv.c\ emv/emv_tags.c\ emv/dol.c\ + emv/emvjson.c\ emv/emvcore.c\ emv/test/crypto_test.c\ emv/test/sda_test.c\ diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 624571a7..23cc5d9c 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -11,57 +11,11 @@ #include #include "mifare.h" #include "cmdemv.h" +#include "emvjson.h" #include "test/cryptotest.h" #include "cliparser/cliparser.h" #include -typedef struct { - tlv_tag_t Tag; - char *Name; -} ApplicationDataElm; - -static const ApplicationDataElm ApplicationData[] = { -{0x82, "AIP"}, -{0x94, "AFL"}, - -{0x5A, "PAN"}, -{0x5F34, "PANSeqNo"}, -{0x5F24, "ExpirationDate"}, -{0x5F25, "EffectiveDate"}, -{0x5F28, "IssuerCountryCode"}, - -{0x50, "ApplicationLabel"}, -{0x9F08, "VersionNumber"}, -{0x9F42, "CurrencyCode"}, -{0x5F2D, "LanguagePreference"}, -{0x87, "PriorityIndicator"}, - -{0x9F07, "AUC"}, // Application Usage Control -{0x9F6C, "CTQ"}, -{0x8E, "CVMList"}, -{0x9F0D, "IACDefault"}, -{0x9F0E, "IACDeny"}, -{0x9F0F, "IACOnline"}, - -{0x8F, "CertificationAuthorityPublicKeyIndex"}, -{0x9F32, "IssuerPublicKeyExponent"}, -{0x92, "IssuerPublicKeyRemainder"}, -{0x90, "IssuerPublicKeyCertificate"}, -{0x9F47, "ICCPublicKeyExponent"}, -{0x9F46, "ICCPublicKeyCertificate"}, - -{0x00, "end..."} -}; -int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm); - -char* GetApplicationDataName(tlv_tag_t tag) { - for (int i = 0; i < ApplicationDataLen; i++) - if (ApplicationData[i].Tag == tag) - return ApplicationData[i].Name; - - return NULL; -} - #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) void ParamLoadDefaults(struct tlvdb *tlvRoot) { //9F02:(Amount, authorized (Numeric)) len:6 @@ -1169,161 +1123,6 @@ int CmdHFEMVExec(const char *cmd) { return 0; } -int JsonSaveStr(json_t *root, char *path, char *value) { - json_error_t error; - - if (strlen(path) < 1) - return 1; - - if (path[0] == '$') { - if (json_path_set(root, path, json_string(value), 0, &error)) { - PrintAndLog("ERROR: can't set json path: ", error.text); - return 2; - } else { - return 0; - } - } else { - return json_object_set_new(root, path, json_string(value)); - } -}; - -int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) { - char * msg = sprint_hex(data, datalen); - if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') - msg[strlen(msg) - 1] = '\0'; - - return JsonSaveStr(elm, path, msg); -} - -int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) { - uint8_t bdata[8] = {0}; - int len = 0; - if (!datalen) { - for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) { - if (!(data & u)) { - break; - } - len++; - } - if (!len) - len = 1; - } else { - len = datalen; - } - num_to_bytes(data, len, bdata); - - return JsonSaveBufAsHex(elm, path, bdata, len); -} - -int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) { - const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm); - if (tlvelm) - return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len); - else - return 1; -} - -int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) { - json_error_t error; - - if (strlen(path) < 1 || !tlvelm) - return 1; - - if (path[0] == '$') { - - json_t *obj = json_path_get(elm, path); - if (!obj) { - obj = json_object(); - - if (json_is_array(elm)) { - if (json_array_append_new(elm, obj)) { - PrintAndLog("ERROR: can't append array: %s", path); - return 2; - } - } else { - if (json_path_set(elm, path, obj, 0, &error)) { - PrintAndLog("ERROR: can't set json path: ", error.text); - return 2; - } - } - } - - if (saveAppDataLink) { - char * AppDataName = GetApplicationDataName(tlvelm->tag); - if (AppDataName) - JsonSaveStr(obj, "appdata", AppDataName); - } else { - char * name = emv_get_tag_name(tlvelm); - if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7)) - JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm)); - JsonSaveHex(obj, "tag", tlvelm->tag, 0); - if (saveValue) { - JsonSaveHex(obj, "length", tlvelm->len, 0); - JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len); - }; - } - } - - return 0; -} - -int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) { - return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink); -} - -int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) { - struct tlvdb *tlvp = tlvdbelm; - while (tlvp) { - const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp); - char * AppDataName = NULL; - if (tlvpelm) - AppDataName = GetApplicationDataName(tlvpelm->tag); - - if (AppDataName) { - char appdatalink[200] = {0}; - sprintf(appdatalink, "$.ApplicationData.%s", AppDataName); - JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len); - } - - json_t *pelm = json_path_get(elm, path); - if (pelm && json_is_array(pelm)) { - json_t *appendelm = json_object(); - json_array_append_new(pelm, appendelm); - JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); - pelm = appendelm; - } else { - JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); - pelm = json_path_get(elm, path); - } - - if (tlvdb_elm_get_children(tlvp)) { - // get path element - if(!pelm) - return 1; - - // check childs element and add it if not found - json_t *chjson = json_path_get(pelm, "$.Childs"); - if (!chjson) { - json_object_set_new(pelm, "Childs", json_array()); - - chjson = json_path_get(pelm, "$.Childs"); - } - - // check - if (!json_is_array(chjson)) { - PrintAndLog("E->Internal logic error. `$.Childs` is not an array."); - break; - } - - // Recursion - JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp)); - } - - tlvp = tlvdb_elm_get_next(tlvp); - } - return 0; -} - int CmdHFEMVScan(const char *cmd) { uint8_t AID[APDU_AID_LEN] = {0}; size_t AIDlen = 0; diff --git a/client/emv/emvjson.c b/client/emv/emvjson.c new file mode 100644 index 00000000..2c0a7a25 --- /dev/null +++ b/client/emv/emvjson.c @@ -0,0 +1,216 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// EMV json logic +//----------------------------------------------------------------------------- + +#include "emvjson.h" +#include +#include +#include +#include +#include +#include "util.h" +#include "ui.h" +#include "emv_tags.h" + +static const ApplicationDataElm ApplicationData[] = { +{0x82, "AIP"}, +{0x94, "AFL"}, + +{0x5A, "PAN"}, +{0x5F34, "PANSeqNo"}, +{0x5F24, "ExpirationDate"}, +{0x5F25, "EffectiveDate"}, +{0x5F28, "IssuerCountryCode"}, + +{0x50, "ApplicationLabel"}, +{0x9F08, "VersionNumber"}, +{0x9F42, "CurrencyCode"}, +{0x5F2D, "LanguagePreference"}, +{0x87, "PriorityIndicator"}, + +{0x9F07, "AUC"}, // Application Usage Control +{0x9F6C, "CTQ"}, +{0x8E, "CVMList"}, +{0x9F0D, "IACDefault"}, +{0x9F0E, "IACDeny"}, +{0x9F0F, "IACOnline"}, + +{0x8F, "CertificationAuthorityPublicKeyIndex"}, +{0x9F32, "IssuerPublicKeyExponent"}, +{0x92, "IssuerPublicKeyRemainder"}, +{0x90, "IssuerPublicKeyCertificate"}, +{0x9F47, "ICCPublicKeyExponent"}, +{0x9F46, "ICCPublicKeyCertificate"}, + +{0x00, "end..."} +}; +int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm); + +char* GetApplicationDataName(tlv_tag_t tag) { + for (int i = 0; i < ApplicationDataLen; i++) + if (ApplicationData[i].Tag == tag) + return ApplicationData[i].Name; + + return NULL; +} + +int JsonSaveStr(json_t *root, char *path, char *value) { + json_error_t error; + + if (strlen(path) < 1) + return 1; + + if (path[0] == '$') { + if (json_path_set(root, path, json_string(value), 0, &error)) { + PrintAndLog("ERROR: can't set json path: ", error.text); + return 2; + } else { + return 0; + } + } else { + return json_object_set_new(root, path, json_string(value)); + } +}; + +int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) { + char * msg = sprint_hex(data, datalen); + if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') + msg[strlen(msg) - 1] = '\0'; + + return JsonSaveStr(elm, path, msg); +} + +int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) { + uint8_t bdata[8] = {0}; + int len = 0; + if (!datalen) { + for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) { + if (!(data & u)) { + break; + } + len++; + } + if (!len) + len = 1; + } else { + len = datalen; + } + num_to_bytes(data, len, bdata); + + return JsonSaveBufAsHex(elm, path, bdata, len); +} + +int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) { + const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm); + if (tlvelm) + return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len); + else + return 1; +} + +int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) { + json_error_t error; + + if (strlen(path) < 1 || !tlvelm) + return 1; + + if (path[0] == '$') { + + json_t *obj = json_path_get(elm, path); + if (!obj) { + obj = json_object(); + + if (json_is_array(elm)) { + if (json_array_append_new(elm, obj)) { + PrintAndLog("ERROR: can't append array: %s", path); + return 2; + } + } else { + if (json_path_set(elm, path, obj, 0, &error)) { + PrintAndLog("ERROR: can't set json path: ", error.text); + return 2; + } + } + } + + if (saveAppDataLink) { + char * AppDataName = GetApplicationDataName(tlvelm->tag); + if (AppDataName) + JsonSaveStr(obj, "appdata", AppDataName); + } else { + char * name = emv_get_tag_name(tlvelm); + if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7)) + JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm)); + JsonSaveHex(obj, "tag", tlvelm->tag, 0); + if (saveValue) { + JsonSaveHex(obj, "length", tlvelm->len, 0); + JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len); + }; + } + } + + return 0; +} + +int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) { + return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink); +} + +int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) { + struct tlvdb *tlvp = tlvdbelm; + while (tlvp) { + const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp); + char * AppDataName = NULL; + if (tlvpelm) + AppDataName = GetApplicationDataName(tlvpelm->tag); + + if (AppDataName) { + char appdatalink[200] = {0}; + sprintf(appdatalink, "$.ApplicationData.%s", AppDataName); + JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len); + } + + json_t *pelm = json_path_get(elm, path); + if (pelm && json_is_array(pelm)) { + json_t *appendelm = json_object(); + json_array_append_new(pelm, appendelm); + JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); + pelm = appendelm; + } else { + JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); + pelm = json_path_get(elm, path); + } + + if (tlvdb_elm_get_children(tlvp)) { + // get path element + if(!pelm) + return 1; + + // check childs element and add it if not found + json_t *chjson = json_path_get(pelm, "$.Childs"); + if (!chjson) { + json_object_set_new(pelm, "Childs", json_array()); + + chjson = json_path_get(pelm, "$.Childs"); + } + + // check + if (!json_is_array(chjson)) { + PrintAndLog("E->Internal logic error. `$.Childs` is not an array."); + break; + } + + // Recursion + JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp)); + } + + tlvp = tlvdb_elm_get_next(tlvp); + } + return 0; +} diff --git a/client/emv/emvjson.h b/client/emv/emvjson.h new file mode 100644 index 00000000..2d08e270 --- /dev/null +++ b/client/emv/emvjson.h @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// EMV json logic +//----------------------------------------------------------------------------- +#ifndef EMVJSON_H__ +#define EMVJSON_H__ + +#include +#include "tlv.h" + +typedef struct { + tlv_tag_t Tag; + char *Name; +} ApplicationDataElm; + +extern char* GetApplicationDataName(tlv_tag_t tag); + +extern int JsonSaveStr(json_t *root, char *path, char *value); +extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen); +extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen); + +extern int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm); +extern int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink); +extern int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink); + +extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm); + +#endif \ No newline at end of file