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\
// convertors
int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
*datalen = 0;
+
+ int ibuf = 0;
+ uint8_t buf[256] = {0};
+ int res = CLIParamStrToBuf(argstr, buf, maxdatalen, &ibuf);
+ if (res || !ibuf)
+ return res;
+
+ switch(param_gethex_to_eol((char *)buf, 0, data, maxdatalen, datalen)) {
+ case 1:
+ printf("Parameter error: Invalid HEX value.\n");
+ return 1;
+ case 2:
+ printf("Parameter error: parameter too large.\n");
+ return 2;
+ case 3:
+ printf("Parameter error: Hex string must have even number of digits.\n");
+ return 3;
+ }
+
+ return 0;
+}
+
+int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
+ *datalen = 0;
if (!argstr->count)
return 0;
- char buf[256] = {0};
+ uint8_t buf[256] = {0};
int ibuf = 0;
for (int i = 0; i < argstr->count; i++) {
if (!ibuf)
return 0;
-
- switch(param_gethex_to_eol(buf, 0, data, maxdatalen, datalen)) {
- case 1:
- printf("Parameter error: Invalid HEX value.\n");
- return 1;
- case 2:
- printf("Parameter error: parameter too large.\n");
+
+ if (ibuf > maxdatalen)
return 2;
- case 3:
- printf("Parameter error: Hex string must have even number of digits.\n");
- return 3;
- }
+
+ memcpy(data, buf, ibuf);
+ *datalen = ibuf;
return 0;
}
-
#define arg_strx0(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 0, 250, (glossary)))
#define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;}
-#define CLIGetStrBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;}
-#define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;}
+#define CLIGetHexBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;}
+#define CLIGetHexWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;}
+#define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;}
extern int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp);
extern int CLIParserParseString(const char* str, void* argtable[], size_t vargtableLen, bool allowEmptyExec);
extern void CLIParserFree();
extern int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen);
+extern int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen);
return 0;
}
+int Hf14443_4aGetCardData(iso14a_card_select_t * card) {
+ UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}};
+ SendCommand(&c);
+
+ UsbCommand resp;
+ WaitForResponse(CMD_ACK,&resp);
+
+ memcpy(card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t));
+
+ uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
+
+ if(select_status == 0) {
+ PrintAndLog("E->iso14443a card select failed");
+ return 1;
+ }
+
+ if(select_status == 2) {
+ PrintAndLog("E->Card doesn't support iso14443-4 mode");
+ return 1;
+ }
+
+ if(select_status == 3) {
+ PrintAndLog("E->Card doesn't support standard iso14443-3 anticollision");
+ PrintAndLog("\tATQA : %02x %02x", card->atqa[1], card->atqa[0]);
+ return 1;
+ }
+
+ PrintAndLog(" UID: %s", sprint_hex(card->uid, card->uidlen));
+ PrintAndLog("ATQA: %02x %02x", card->atqa[1], card->atqa[0]);
+ PrintAndLog(" SAK: %02x [%" PRIu64 "]", card->sak, resp.arg[0]);
+ if(card->ats_len < 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
+ PrintAndLog("E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len));
+ return 1;
+ }
+ PrintAndLog(" ATS: %s", sprint_hex(card->ats, card->ats_len));
+
+ return 0;
+}
+
int CmdHF14AReader(const char *Cmd) {
uint32_t cm = ISO14A_CONNECT;
bool leaveSignalON = false;
leaveSignalON = arg_get_lit(2);
decodeTLV = arg_get_lit(3);
// len = data + PCB(1b) + CRC(2b)
- CLIGetStrBLessWithReturn(4, data, &datalen, 1 + 2);
+ CLIGetHexBLessWithReturn(4, data, &datalen, 1 + 2);
CLIParserFree();
#include <stdint.h>
#include <stdbool.h>
+#include "mifare.h"
int CmdHF14A(const char *Cmd);
int CmdHF14AList(const char *Cmd);
char* getTagInfo(uint8_t uid);
extern void DropField();
+
+extern int Hf14443_4aGetCardData(iso14a_card_select_t * card);
extern int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
//-----------------------------------------------------------------------------
#include <ctype.h>
+#include "mifare.h"
#include "cmdemv.h"
+#include "emvjson.h"
+#include "emv_pki.h"
#include "test/cryptotest.h"
#include "cliparser/cliparser.h"
#include <jansson.h>
-bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) {
- int buflen = 0;
-
- switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) {
- case 1:
- PrintAndLog("%s Invalid HEX value.", errormsg);
- return false;
- case 2:
- PrintAndLog("%s Hex value too large.", errormsg);
- return false;
- case 3:
- PrintAndLog("%s Hex value must have even number of digits.", errormsg);
- return false;
- }
-
- if (buflen > maxbufferlen) {
- PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen);
- return false;
- }
-
- *bufferlen = buflen;
-
- return true;
-}
-
#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
TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
}
-bool ParamLoadFromJson(struct tlvdb *tlv) {
- json_t *root;
- json_error_t error;
-
- if (!tlv) {
- PrintAndLog("ERROR load params: tlv tree is NULL.");
- return false;
- }
-
- // current path + file name
- const char *relfname = "emv/defparams.json";
- char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1];
- strcpy(fname, get_my_executable_directory());
- strcat(fname, relfname);
-
- root = json_load_file(fname, 0, &error);
- if (!root) {
- PrintAndLog("Load params: json error on line %d: %s", error.line, error.text);
- return false;
- }
-
- if (!json_is_array(root)) {
- PrintAndLog("Load params: Invalid json format. root must be array.");
- return false;
- }
-
- PrintAndLog("Load params: json OK");
-
- for(int i = 0; i < json_array_size(root); i++) {
- json_t *data, *jtype, *jlength, *jvalue;
-
- data = json_array_get(root, i);
- if(!json_is_object(data))
- {
- PrintAndLog("Load params: data [%d] is not an object", i + 1);
- json_decref(root);
- return false;
- }
-
- jtype = json_object_get(data, "type");
- if(!json_is_string(jtype))
- {
- PrintAndLog("Load params: data [%d] type is not a string", i + 1);
- json_decref(root);
- return false;
- }
- const char *tlvType = json_string_value(jtype);
-
- jvalue = json_object_get(data, "value");
- if(!json_is_string(jvalue))
- {
- PrintAndLog("Load params: data [%d] value is not a string", i + 1);
- json_decref(root);
- return false;
- }
- const char *tlvValue = json_string_value(jvalue);
-
- jlength = json_object_get(data, "length");
- if(!json_is_number(jlength))
- {
- PrintAndLog("Load params: data [%d] length is not a number", i + 1);
- json_decref(root);
- return false;
- }
-
- int tlvLength = json_integer_value(jlength);
- if (tlvLength > 250) {
- PrintAndLog("Load params: data [%d] length more than 250", i + 1);
- json_decref(root);
- return false;
- }
-
- PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue);
- uint8_t buf[251] = {0};
- size_t buflen = 0;
-
- // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t...
- if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) {
- json_decref(root);
- return false;
- }
- tlv_tag_t tag = 0;
- for (int i = 0; i < buflen; i++) {
- tag = (tag << 8) + buf[i];
- }
-
- if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) {
- json_decref(root);
- return false;
- }
-
- if (buflen != tlvLength) {
- PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength);
- json_decref(root);
- return false;
- }
-
- tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf);
- }
-
- json_decref(root);
-
- return true;
-}
-
int CmdHFEMVSelect(const char *cmd) {
uint8_t data[APDU_AID_LEN] = {0};
int datalen = 0;
bool leaveSignalON = arg_get_lit(2);
bool APDULogging = arg_get_lit(3);
bool decodeTLV = arg_get_lit(4);
- CLIGetStrWithReturn(5, data, &datalen);
+ CLIGetHexWithReturn(5, data, &datalen);
CLIParserFree();
SetAPDULogging(APDULogging);
bool dataMakeFromPDOL = arg_get_lit(3);
bool APDULogging = arg_get_lit(4);
bool decodeTLV = arg_get_lit(5);
- CLIGetStrWithReturn(6, data, &datalen);
+ CLIGetHexWithReturn(6, data, &datalen);
CLIParserFree();
SetAPDULogging(APDULogging);
bool leaveSignalON = arg_get_lit(1);
bool APDULogging = arg_get_lit(2);
bool decodeTLV = arg_get_lit(3);
- CLIGetStrWithReturn(4, data, &datalen);
+ CLIGetHexWithReturn(4, data, &datalen);
CLIParserFree();
if (datalen != 2) {
bool dataMakeFromCDOL = arg_get_lit(5);
bool APDULogging = arg_get_lit(6);
bool decodeTLV = arg_get_lit(7);
- CLIGetStrWithReturn(8, data, &datalen);
+ CLIGetHexWithReturn(8, data, &datalen);
CLIParserFree();
SetAPDULogging(APDULogging);
bool dataMakeFromDDOL = arg_get_lit(3);
bool APDULogging = arg_get_lit(4);
bool decodeTLV = arg_get_lit(5);
- CLIGetStrWithReturn(6, data, &datalen);
+ CLIGetHexWithReturn(6, data, &datalen);
CLIParserFree();
SetAPDULogging(APDULogging);
#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
+void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) {
+
+ ParamLoadDefaults(tlvRoot);
+
+ if (paramLoadJSON) {
+ PrintAndLog("* * Transaction parameters loading from JSON...");
+ ParamLoadFromJson(tlvRoot);
+ }
+
+ //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
+ char *qVSDC = "\x26\x00\x00\x00";
+ if (GenACGPO) {
+ qVSDC = "\x26\x80\x00\x00";
+ }
+ switch(TrType) {
+ case TT_MSD:
+ TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
+ break;
+ // not standard for contactless. just for test.
+ case TT_VSDC:
+ TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
+ break;
+ case TT_QVSDCMCHIP:
+ TLV_ADD(0x9F66, qVSDC); // qVSDC
+ break;
+ case TT_CDA:
+ TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled)
+ break;
+ default:
+ break;
+ }
+}
+
+void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, bool decodeTLV) {
+ if (buf[0] == 0x80) {
+ if (decodeTLV){
+ PrintAndLog("GPO response format1:");
+ TLVPrintFromBuffer(buf, len);
+ }
+
+ if (len < 4 || (len - 4) % 4) {
+ PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len);
+ } else {
+ // AIP
+ struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2);
+ tlvdb_add(tlvRoot, f1AIP);
+ if (decodeTLV){
+ PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:");
+ TLVPrintFromTLV(f1AIP);
+ }
+
+ // AFL
+ struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2);
+ tlvdb_add(tlvRoot, f1AFL);
+ if (decodeTLV)
+ TLVPrintFromTLV(f1AFL);
+ }
+ } else {
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+ }
+}
+
int CmdHFEMVExec(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
PrintAndLog("* Selected.");
PrintAndLog("\n* Init transaction parameters.");
-
- ParamLoadDefaults(tlvRoot);
-
- if (paramLoadJSON) {
- PrintAndLog("* * Transaction parameters loading from JSON...");
- ParamLoadFromJson(tlvRoot);
- }
-
- //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
- char *qVSDC = "\x26\x00\x00\x00";
- if (GenACGPO) {
- qVSDC = "\x26\x80\x00\x00";
- }
- switch(TrType) {
- case TT_MSD:
- TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
- break;
- // not standard for contactless. just for test.
- case TT_VSDC:
- TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
- break;
- case TT_QVSDCMCHIP:
- TLV_ADD(0x9F66, qVSDC); // qVSDC
- break;
- case TT_CDA:
- TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled)
- break;
- default:
- break;
- }
-
+ InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO);
TLVPrintFromTLV(tlvRoot); // TODO delete!!!
PrintAndLog("\n* Calc PDOL.");
}
// process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV]
- if (buf[0] == 0x80) {
- if (decodeTLV){
- PrintAndLog("GPO response format1:");
- TLVPrintFromBuffer(buf, len);
- }
-
- if (len < 4 || (len - 4) % 4) {
- PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len);
- } else {
- // AIP
- struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2);
- tlvdb_add(tlvRoot, f1AIP);
- if (decodeTLV){
- PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:");
- TLVPrintFromTLV(f1AIP);
- }
-
- // AFL
- struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2);
- tlvdb_add(tlvRoot, f1AFL);
- if (decodeTLV)
- TLVPrintFromTLV(f1AFL);
- }
- } else {
- if (decodeTLV)
- TLVPrintFromBuffer(buf, len);
- }
+ ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV);
// extract PAN from track2
{
return 0;
}
-int UsageCmdHFEMVScan(void) {
- PrintAndLog("HELP : Scan EMV card and save it contents to a file. \n");
- PrintAndLog(" It executes EMV contactless transaction and saves result to a file which can be used for emulation.\n");
- PrintAndLog("Usage: hf emv scan [-a][-t][-v][-c][-x][-g] <file_name>\n");
- PrintAndLog(" Options:");
- PrintAndLog(" -a : show APDU reqests and responses\n");
- PrintAndLog(" -t : TLV decode results\n");
- PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n");
- PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n");
- PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n");
- PrintAndLog(" -g : VISA. generate AC from GPO\n");
- PrintAndLog("By default : transaction type - MSD.\n");
- PrintAndLog("Samples:");
- PrintAndLog(" hf emv scan -a -t -> scan MSD transaction mode");
- PrintAndLog(" hf emv scan -a -t -c -> scan CDA transaction mode");
- return 0;
-}
-
int CmdHFEMVScan(const char *cmd) {
- UsageCmdHFEMVScan();
+ uint8_t AID[APDU_AID_LEN] = {0};
+ size_t AIDlen = 0;
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res;
+ json_t *root;
+ json_error_t error;
+
+ CLIParserInit("hf emv scan",
+ "Scan EMV card and save it contents to a file.",
+ "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n"
+ "Usage:\n\thf emv scan -at -> scan MSD transaction mode and show APDU and TLV\n"
+ "\thf emv scan -c -> scan CDA transaction mode\n");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("aA", "apdu", "show APDU reqests and responses."),
+ arg_lit0("tT", "tlv", "TLV decode results."),
+ arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"),
+ arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."),
+ arg_rem("By default:", "Transaction type - MSD"),
+ arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."),
+ arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."),
+ arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."),
+ arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."),
+ arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"),
+ arg_str1(NULL, NULL, "output.json", "JSON output file name"),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool showAPDU = arg_get_lit(1);
+ bool decodeTLV = arg_get_lit(2);
+ bool extractTLVElements = arg_get_lit(3);
+ bool paramLoadJSON = arg_get_lit(4);
+
+ enum TransactionType TrType = TT_MSD;
+ if (arg_get_lit(6))
+ TrType = TT_QVSDCMCHIP;
+ if (arg_get_lit(7))
+ TrType = TT_CDA;
+ if (arg_get_lit(8))
+ TrType = TT_VSDC;
+
+ bool GenACGPO = arg_get_lit(9);
+ bool MergeJSON = arg_get_lit(10);
+ uint8_t relfname[250] ={0};
+ char *crelfname = (char *)relfname;
+ int relfnamelen = 0;
+ CLIGetStrWithReturn(11, relfname, &relfnamelen);
+ CLIParserFree();
+
+ SetAPDULogging(showAPDU);
+
+ // current path + file name
+ if (!strstr(crelfname, ".json"))
+ strcat(crelfname, ".json");
+ char fname[strlen(get_my_executable_directory()) + strlen(crelfname) + 1];
+ strcpy(fname, get_my_executable_directory());
+ strcat(fname, crelfname);
+
+ if (MergeJSON) {
+ root = json_load_file(fname, 0, &error);
+ if (!root) {
+ PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
+ return 1;
+ }
+
+ if (!json_is_object(root)) {
+ PrintAndLog("ERROR: Invalid json format. root must be an object.");
+ return 1;
+ }
+ } else {
+ root = json_object();
+ }
+
+ // drop field at start
+ DropField();
+
+ // iso 14443 select
+ PrintAndLog("--> GET UID, ATS.");
+
+ iso14a_card_select_t card;
+ if (Hf14443_4aGetCardData(&card)) {
+ return 2;
+ }
+
+ JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`");
+
+ JsonSaveStr(root, "$.Card.Communication", "iso14443-4a");
+ JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen);
+ JsonSaveHex(root, "$.Card.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2);
+ JsonSaveHex(root, "$.Card.SAK", card.sak, 0);
+ JsonSaveBufAsHex(root, "$.Card.ATS", (uint8_t *)card.ats, card.ats_len);
+
+ // init applets list tree
+ const char *al = "Applets list";
+ struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
+
+ // EMV PPSE
+ PrintAndLog("--> PPSE.");
+ res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw);
+
+ if (!res && sw == 0x9000){
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ JsonSaveBufAsHex(root, "$.PPSE.AID", (uint8_t *)"2PAY.SYS.DDF01", 14);
+
+ struct tlvdb *fci = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, root, "$.PPSE.FCITemplate", fci);
+ else
+ JsonSaveTLVTreeElm(root, "$.PPSE.FCITemplate", fci, true, true, false);
+ JsonSaveTLVValue(root, "$.Application.KernelID", tlvdb_find_full(fci, 0x9f2a));
+ tlvdb_free(fci);
+ }
+
+ res = EMVSearchPSE(false, true, decodeTLV, tlvSelect);
+
+ // check PPSE and select application id
+ if (!res) {
+ TLVPrintAIDlistFromSelectTLV(tlvSelect);
+ } else {
+ // EMV SEARCH with AID list
+ SetAPDULogging(false);
+ PrintAndLog("--> AID search.");
+ if (EMVSearch(false, true, decodeTLV, tlvSelect)) {
+ PrintAndLog("E->Can't found any of EMV AID. Exit...");
+ tlvdb_free(tlvSelect);
+ DropField();
+ return 3;
+ }
+
+ // check search and select application id
+ TLVPrintAIDlistFromSelectTLV(tlvSelect);
+ }
+
+ // EMV SELECT application
+ SetAPDULogging(showAPDU);
+ EMVSelectApplication(tlvSelect, AID, &AIDlen);
+
+ tlvdb_free(tlvSelect);
+
+ if (!AIDlen) {
+ PrintAndLog("Can't select AID. EMV AID not found. Exit...");
+ DropField();
+ return 4;
+ }
+
+ JsonSaveBufAsHex(root, "$.Application.AID", AID, AIDlen);
+
+ // Init TLV tree
+ const char *alr = "Root terminal TLV tree";
+ struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
+
+ // EMV SELECT applet
+
+ PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen));
+ SetAPDULogging(showAPDU);
+ res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot);
+
+ if (res) {
+ PrintAndLog("E->Can't select AID (%d). Exit...", res);
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 5;
+ }
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ // save mode
+ if (tlvdb_get(tlvRoot, 0x9f38, NULL)) {
+ JsonSaveStr(root, "$.Application.Mode", TransactionTypeStr[TrType]);
+ }
+
+ struct tlvdb *fci = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, root, "$.Application.FCITemplate", fci);
+ else
+ JsonSaveTLVTreeElm(root, "$.Application.FCITemplate", fci, true, true, false);
+ tlvdb_free(fci);
+
+ // create transaction parameters
+ PrintAndLog("-->Init transaction parameters.");
+ InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO);
+
+ PrintAndLog("-->Calc PDOL.");
+ struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
+ if (!pdol_data_tlv){
+ PrintAndLog("E->Can't create PDOL TLV.");
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 6;
+ }
+
+ size_t pdol_data_tlv_data_len;
+ unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
+ if (!pdol_data_tlv_data) {
+ PrintAndLog("E->Can't create PDOL data.");
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 6;
+ }
+ PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
+
+ PrintAndLog("-->GPO.");
+ res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
+
+ free(pdol_data_tlv_data);
+ free(pdol_data_tlv);
+
+ if (res) {
+ PrintAndLog("GPO error(%d): %4x. Exit...", res, sw);
+ tlvdb_free(tlvRoot);
+ DropField();
+ return 7;
+ }
+ ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV);
+
+ struct tlvdb *gpofci = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, root, "$.Application.GPO", gpofci);
+ else
+ JsonSaveTLVTreeElm(root, "$.Application.GPO", gpofci, true, true, false);
+
+ JsonSaveTLVValue(root, "$.ApplicationData.AIP", tlvdb_find_full(gpofci, 0x82));
+ JsonSaveTLVValue(root, "$.ApplicationData.AFL", tlvdb_find_full(gpofci, 0x94));
+
+ tlvdb_free(gpofci);
+
+ PrintAndLog("-->Read records from AFL.");
+ const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL);
+
+ while(AFL && AFL->len) {
+ if (AFL->len % 4) {
+ PrintAndLog("E->Wrong AFL length: %d", AFL->len);
+ break;
+ }
+
+ json_t *sfijson = json_path_get(root, "$.Application.Records");
+ if (!sfijson) {
+ json_t *app = json_path_get(root, "$.Application");
+ json_object_set_new(app, "Records", json_array());
+
+ sfijson = json_path_get(root, "$.Application.Records");
+ }
+ if (!json_is_array(sfijson)) {
+ PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array.");
+ break;
+ }
+ for (int i = 0; i < AFL->len / 4; i++) {
+ uint8_t SFI = AFL->value[i * 4 + 0] >> 3;
+ uint8_t SFIstart = AFL->value[i * 4 + 1];
+ uint8_t SFIend = AFL->value[i * 4 + 2];
+ uint8_t SFIoffline = AFL->value[i * 4 + 3];
+
+ PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline);
+ if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) {
+ PrintAndLog("SFI ERROR! Skipped...");
+ continue;
+ }
+
+ for(int n = SFIstart; n <= SFIend; n++) {
+ PrintAndLog("---->SFI[%02x] %d", SFI, n);
+
+ res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot);
+ if (res) {
+ PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw);
+ continue;
+ }
+
+ if (decodeTLV) {
+ TLVPrintFromBuffer(buf, len);
+ PrintAndLog("");
+ }
+
+ json_t *jsonelm = json_object();
+ json_array_append_new(sfijson, jsonelm);
+
+ JsonSaveHex(jsonelm, "SFI", SFI, 1);
+ JsonSaveHex(jsonelm, "RecordNum", n, 1);
+ JsonSaveHex(jsonelm, "Offline", SFIoffline, 1);
+
+ struct tlvdb *rsfi = tlvdb_parse_multi(buf, len);
+ if (extractTLVElements)
+ JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi);
+ else
+ JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false);
+ tlvdb_free(rsfi);
+ }
+ }
+
+ break;
+ }
+
+ // getting certificates
+ if (tlvdb_get(tlvRoot, 0x90, NULL)) {
+ PrintAndLog("-->Recovering certificates.");
+ PKISetStrictExecution(false);
+ RecoveryCertificates(tlvRoot, root);
+ PKISetStrictExecution(true);
+ }
+
+ // free tlv object
+ tlvdb_free(tlvRoot);
+
+ // DropField
+ DropField();
+
+ res = json_dump_file(root, fname, JSON_INDENT(2));
+ if (res) {
+ PrintAndLog("ERROR: can't save the file: %s", fname);
+ return 200;
+ }
+ PrintAndLog("File `%s` saved.", fname);
+
+ // free json object
+ json_decref(root);
return 0;
}
{"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."},
{"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."},
{"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."},
-// {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."},
+ {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."},
{"test", CmdHFEMVTest, 0, "Crypto logic test."},
{NULL, NULL, 0, NULL}
};
[
{
"name": "Transaction Date",
- "type": "9A",
+ "tag": "9A",
"value": "00 00 00",
"length": 3,
"hint": "format: YYMMDD"
},
{
"name": "Transaction Type",
- "type": "9C",
+ "tag": "9C",
"value": "00",
"length": 1,
"hint": "00: Goods and service, 01: Cash"
},
{
"name": "Amount, authorized",
- "type": "9F 02",
+ "tag": "9F 02",
"value": "00 00 00 00 01 00",
"length": 6,
"hint": "amount (numberic) in cents"
},
{
"name": "Transaction Currency Code",
- "type": "5F 2A",
+ "tag": "5F 2A",
"value": "09 80",
"length": 2,
"hint": "USD 840, EUR 978, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999"
},
{
"name": "Terminal Country Code",
- "type": "9F 1A",
+ "tag": "9F 1A",
"value": "72 75",
"length": 2,
"hint": "ISO3166: de, en (65 6e), uk(75 6b), ru (72 75), us, ua"
},
{
"name": "Terminal Transaction Qualifiers (TTQ)",
- "type": "9F 66",
+ "tag": "9F 66",
"value": "26 00 00 00",
"length": 4,
"hint": "qVSDC 26 00 00 00, gen AC from GPO 26 80 00 00, MSD 86 00 00 00, VSDC 46 00 00 00"
},
{
"name": "Unpredictable Number",
- "type": "9F 37",
+ "tag": "9F 37",
"value": "01 02 03 04",
"length": 4,
"hint": "4 byte random number"
},
{
"name": "Unpredictable Number (MSD for UDOL)",
- "type": "9F 6A",
+ "tag": "9F 6A",
"value": "01 02 03 05",
"length": 4,
"hint": "4 byte random number"
#include <string.h>
#include <stdarg.h>
+static bool strictExecution = true;
+void PKISetStrictExecution(bool se) {
+ strictExecution = se;
+}
+
static const unsigned char empty_tlv_value[] = {};
static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value};
printf("ERROR: Calculated wrong hash\n");
printf("decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len));
printf("calculated: %s\n",sprint_hex(crypto_hash_read(ch), hash_len));
- crypto_hash_close(ch);
- free(data);
- return NULL;
+
+ if (strictExecution) {
+ crypto_hash_close(ch);
+ free(data);
+ return NULL;
+ }
}
crypto_hash_close(ch);
#include <stddef.h>
+extern void PKISetStrictExecution(bool se);
+
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db);
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv);
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db);
return true;
}
+
+char *emv_get_tag_name(const struct tlv *tlv)
+{
+ static char *defstr = "";
+
+ if (!tlv)
+ return defstr;
+
+ const struct emv_tag *tag = emv_get_tag(tlv);
+ if (tag)
+ return tag->name;
+
+ return defstr;
+}
# define EMVCID_REASON_MASK 0x07
bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level);
+char *emv_get_tag_name(const struct tlv *tlv);
#endif
//-----------------------------------------------------------------------------
#include "emvcore.h"
+#include "emvjson.h"
// Got from here. Thanks)
// https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix
};
//static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*);
+char *TransactionTypeStr[] = {
+ "MSD",
+ "VSDC",
+ "qVCDCMCHIP",
+ "CDA"
+};
+
typedef struct {
enum CardPSVendor vendor;
const char* aid;
emv_pk_free(icc_pk);
return 0;
}
+
+int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) {
+
+ struct emv_pk *pk = get_ca_pk(tlvRoot);
+ if (!pk) {
+ PrintAndLog("ERROR: Key not found. Exit.");
+ return 1;
+ }
+
+ struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlvRoot);
+ if (!issuer_pk) {
+ emv_pk_free(pk);
+ PrintAndLog("WARNING: Issuer certificate not found. Exit.");
+ return 2;
+ }
+ PrintAndLog("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx",
+ issuer_pk->rid[0],
+ issuer_pk->rid[1],
+ issuer_pk->rid[2],
+ issuer_pk->rid[3],
+ issuer_pk->rid[4],
+ issuer_pk->index,
+ issuer_pk->serial[0],
+ issuer_pk->serial[1],
+ issuer_pk->serial[2]
+ );
+
+ JsonSaveBufAsHex(root, "$.ApplicationData.RID", issuer_pk->rid, 5);
+
+ char *issuer_pk_c = emv_pk_dump_pk(issuer_pk);
+ JsonSaveStr(root, "$.ApplicationData.IssuerPublicKeyDec", issuer_pk_c);
+ JsonSaveBufAsHex(root, "$.ApplicationData.IssuerPublicKeyModulus", issuer_pk->modulus, issuer_pk->mlen);
+ free(issuer_pk_c);
+
+ struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, NULL);
+ if (!icc_pk) {
+ emv_pk_free(pk);
+ emv_pk_free(issuer_pk);
+ PrintAndLog("WARNING: ICC certificate not found. Exit.");
+ return 2;
+ }
+ printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n",
+ icc_pk->rid[0],
+ icc_pk->rid[1],
+ icc_pk->rid[2],
+ icc_pk->rid[3],
+ icc_pk->rid[4],
+ icc_pk->index,
+ icc_pk->serial[0],
+ icc_pk->serial[1],
+ icc_pk->serial[2]
+ );
+
+ char *icc_pk_c = emv_pk_dump_pk(icc_pk);
+ JsonSaveStr(root, "$.ApplicationData.ICCPublicKeyDec", icc_pk_c);
+ JsonSaveBufAsHex(root, "$.ApplicationData.ICCPublicKeyModulus", icc_pk->modulus, icc_pk->mlen);
+ free(issuer_pk_c);
+
+ return 0;
+}
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
+#include <jansson.h>
#include "util.h"
#include "common.h"
#include "ui.h"
TT_QVSDCMCHIP,
TT_CDA,
};
+extern char *TransactionTypeStr[];
typedef struct {
uint8_t CLA;
extern int trDDA(bool decodeTLV, struct tlvdb *tlv);
extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv);
+extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root);
+
#endif
--- /dev/null
+//-----------------------------------------------------------------------------
+// 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include "util.h"
+#include "ui.h"
+#include "proxmark3.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"},
+{0x9F36, "ATC"}, //Application Transaction Counter
+
+{0x5F20, "CardholderName"},
+
+{0x9F38, "PDOL"},
+{0x8C, "CDOL1"},
+{0x8D, "CDOL2"},
+
+{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;
+}
+
+bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) {
+ int buflen = 0;
+
+ switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) {
+ case 1:
+ PrintAndLog("%s Invalid HEX value.", errormsg);
+ return false;
+ case 2:
+ PrintAndLog("%s Hex value too large.", errormsg);
+ return false;
+ case 3:
+ PrintAndLog("%s Hex value must have even number of digits.", errormsg);
+ return false;
+ }
+
+ if (buflen > maxbufferlen) {
+ PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen);
+ return false;
+ }
+
+ *bufferlen = buflen;
+
+ return true;
+}
+
+bool ParamLoadFromJson(struct tlvdb *tlv) {
+ json_t *root;
+ json_error_t error;
+
+ if (!tlv) {
+ PrintAndLog("ERROR load params: tlv tree is NULL.");
+ return false;
+ }
+
+ // current path + file name
+ const char *relfname = "emv/defparams.json";
+ char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1];
+ strcpy(fname, get_my_executable_directory());
+ strcat(fname, relfname);
+
+ root = json_load_file(fname, 0, &error);
+ if (!root) {
+ PrintAndLog("Load params: json error on line %d: %s", error.line, error.text);
+ return false;
+ }
+
+ if (!json_is_array(root)) {
+ PrintAndLog("Load params: Invalid json format. root must be array.");
+ return false;
+ }
+
+ PrintAndLog("Load params: json(%d) OK", json_array_size(root));
+
+ for(int i = 0; i < json_array_size(root); i++) {
+ json_t *data, *jtag, *jlength, *jvalue;
+
+ data = json_array_get(root, i);
+ if(!json_is_object(data))
+ {
+ PrintAndLog("Load params: data [%d] is not an object", i + 1);
+ json_decref(root);
+ return false;
+ }
+
+ jtag = json_object_get(data, "tag");
+ if(!json_is_string(jtag))
+ {
+ PrintAndLog("Load params: data [%d] tag is not a string", i + 1);
+ json_decref(root);
+ return false;
+ }
+ const char *tlvTag = json_string_value(jtag);
+
+ jvalue = json_object_get(data, "value");
+ if(!json_is_string(jvalue))
+ {
+ PrintAndLog("Load params: data [%d] value is not a string", i + 1);
+ json_decref(root);
+ return false;
+ }
+ const char *tlvValue = json_string_value(jvalue);
+
+ jlength = json_object_get(data, "length");
+ if(!json_is_number(jlength))
+ {
+ PrintAndLog("Load params: data [%d] length is not a number", i + 1);
+ json_decref(root);
+ return false;
+ }
+
+ int tlvLength = json_integer_value(jlength);
+ if (tlvLength > 250) {
+ PrintAndLog("Load params: data [%d] length more than 250", i + 1);
+ json_decref(root);
+ return false;
+ }
+
+ PrintAndLog("TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue);
+ uint8_t buf[251] = {0};
+ size_t buflen = 0;
+
+ // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t...
+ if (!HexToBuffer("TLV Error type:", tlvTag, buf, 2, &buflen)) {
+ json_decref(root);
+ return false;
+ }
+ tlv_tag_t tag = 0;
+ for (int i = 0; i < buflen; i++) {
+ tag = (tag << 8) + buf[i];
+ }
+
+ if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) {
+ json_decref(root);
+ return false;
+ }
+
+ if (buflen != tlvLength) {
+ PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength);
+ json_decref(root);
+ return false;
+ }
+
+ tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf);
+ }
+
+ json_decref(root);
+
+ return true;
+}
+
--- /dev/null
+//-----------------------------------------------------------------------------
+// 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 <jansson.h>
+#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);
+
+extern bool ParamLoadFromJson(struct tlvdb *tlv);
+
+#endif
\ No newline at end of file
return tlvdb_get(tlvdb, tag, prev);
}
+const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) {
+ return &tlvdb->tag;
+}
+
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len)
{
size_t size = tlv->len;
return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len);
}
+
+struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb)
+{
+ return tlvdb->next;
+}
+
+struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb)
+{
+ return tlvdb->children;
+}
+
+struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb)
+{
+ return tlvdb->parent;
+}
struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len);
void tlvdb_free(struct tlvdb *tlvdb);
+struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb);
+struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb);
+struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb);
+
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens
struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag);
struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag);
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level);
const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
+const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb);
bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv);
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len);
strconv.c \
utf.c \
utf.h \
+ path.c \
value.c
libjansson_la_LDFLAGS = \
-no-undefined \
json_t *json_copy(json_t *value);
json_t *json_deep_copy(const json_t *value);
+/* path */
+
+json_t *json_path_get(const json_t *json, const char *path);
+int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error);
+
+static JSON_INLINE
+int json_path_set(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error)
+{
+ return json_path_set_new(json, path, json_incref(value), flags, error);
+}
/* decoding */
--- /dev/null
+/*
+ * Copyright (c) 2012 Rogerz Zhang <rogerz.zhang@gmail.com>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ *
+ * source here https://github.com/rogerz/jansson/blob/json_path/src/path.c
+ */
+
+#include <string.h>
+#include <assert.h>
+
+#include <jansson.h>
+#include "jansson_private.h"
+
+
+json_t *json_path_get(const json_t *json, const char *path)
+{
+ static const char root_chr = '$', array_open = '[';
+ static const char *path_delims = ".[", *array_close = "]";
+ const json_t *cursor;
+ char *token, *buf, *peek, *endptr, delim = '\0';
+ const char *expect;
+
+ if (!json || !path || path[0] != root_chr)
+ return NULL;
+ else
+ buf = jsonp_strdup(path);
+
+ peek = buf + 1;
+ cursor = json;
+ token = NULL;
+ expect = path_delims;
+
+ while (peek && *peek && cursor)
+ {
+ char *last_peek = peek;
+ peek = strpbrk(peek, expect);
+ if (peek) {
+ if (!token && peek != last_peek)
+ goto fail;
+ delim = *peek;
+ *peek++ = '\0';
+ } else if (expect != path_delims || !token) {
+ goto fail;
+ }
+
+ if (expect == path_delims) {
+ if (token) {
+ cursor = json_object_get(cursor, token);
+ }
+ expect = (delim == array_open ? array_close : path_delims);
+ token = peek;
+ } else if (expect == array_close) {
+ size_t index = strtol(token, &endptr, 0);
+ if (*endptr)
+ goto fail;
+ cursor = json_array_get(cursor, index);
+ token = NULL;
+ expect = path_delims;
+ } else {
+ goto fail;
+ }
+ }
+
+ jsonp_free(buf);
+ return (json_t *)cursor;
+fail:
+ jsonp_free(buf);
+ return NULL;
+}
+
+int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error)
+{
+ static const char root_chr = '$', array_open = '[', object_delim = '.';
+ static const char * const path_delims = ".[", *array_close = "]";
+
+ json_t *cursor, *parent = NULL;
+ char *token, *buf = NULL, *peek, delim = '\0';
+ const char *expect;
+ int index_saved = -1;
+
+ jsonp_error_init(error, "<path>");
+
+ if (!json || !path || flags || !value) {
+ jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument");
+ goto fail;
+ } else {
+ buf = jsonp_strdup(path);
+ }
+
+ if (buf[0] != root_chr) {
+ jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $");
+ goto fail;
+ }
+
+ peek = buf + 1;
+ cursor = json;
+ token = NULL;
+ expect = path_delims;
+
+ while (peek && *peek && cursor)
+ {
+ char *last_peek = peek;
+ peek = strpbrk(last_peek, expect);
+
+ if (peek) {
+ if (!token && peek != last_peek) {
+ jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars");
+ goto fail;
+ }
+ delim = *peek;
+ *peek++ = '\0';
+ } else { // end of path
+ if (expect == path_delims) {
+ break;
+ } else {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "missing ']'?");
+ goto fail;
+ }
+ }
+
+ if (expect == path_delims) {
+ if (token) {
+ if (token[0] == '\0') {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token");
+ goto fail;
+ }
+
+ parent = cursor;
+ cursor = json_object_get(parent, token);
+
+ if (!cursor) {
+ if (!json_is_object(parent)) {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected");
+ goto fail;
+ }
+ if (delim == object_delim) {
+ cursor = json_object();
+ json_object_set_new(parent, token, cursor);
+ } else {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed");
+ goto fail;
+ }
+ }
+ }
+ expect = (delim == array_open ? array_close : path_delims);
+ token = peek;
+ } else if (expect == array_close) {
+ char *endptr;
+ size_t index;
+
+ parent = cursor;
+ if (!json_is_array(parent)) {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected");
+ goto fail;
+ }
+ index = strtol(token, &endptr, 0);
+ if (*endptr) {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index");
+ goto fail;
+ }
+ cursor = json_array_get(parent, index);
+ if (!cursor) {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound");
+ goto fail;
+ }
+ index_saved = index;
+ token = NULL;
+ expect = path_delims;
+ } else {
+ assert(1);
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move");
+ goto fail;
+ }
+ }
+
+ if (token) {
+ if (json_is_object(cursor)) {
+ json_object_set(cursor, token, value);
+ } else {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected");
+ goto fail;
+ }
+ cursor = json_object_get(cursor, token);
+ } else if (index_saved != -1 && json_is_array(parent)) {
+ json_array_set(parent, index_saved, value);
+ cursor = json_array_get(parent, index_saved);
+ } else {
+ jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid path");
+ goto fail;
+ }
+
+ json_decref(value);
+ jsonp_free(buf);
+ return 0;
+
+fail:
+ json_decref(value);
+ jsonp_free(buf);
+ return -1;
+}