From 7710983be338398f3278666c2c67bb9b3517dac0 Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 27 Oct 2017 18:45:42 +0300 Subject: [PATCH 01/23] added `hf 14a apdu` command. --- client/cmdhf14a.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index e3f1a5f1..91fcdeb1 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -554,6 +554,19 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +int CmdHF14AAPDU(const char *cmd) { + if (strlen(cmd)<2) { + PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); + PrintAndLog(" -s activate field and select card"); + PrintAndLog(" -k leave the signal field ON after receive response"); + PrintAndLog(" -t executes TLV decoder if it possible"); + return 0; + } + + + + return 0; +} int CmdHF14ACmdRaw(const char *cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; @@ -759,6 +772,7 @@ static command_t CommandTable[] = {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, + {"apdu", CmdHF14AAPDU, 0, "Send ISO 1443-4 APDU to tag"}, {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, {NULL, NULL, 0, NULL} }; From 1208cdcb45e8a71ec47dfbd579c57ecff1252c5d Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 27 Oct 2017 19:02:45 +0300 Subject: [PATCH 02/23] added sketch for command `hf 14a apdu` --- client/cmdhf14a.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 91fcdeb1..d84a8f35 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -555,6 +555,10 @@ int CmdHF14ASnoop(const char *Cmd) { } int CmdHF14AAPDU(const char *cmd) { + uint8_t data[USB_CMD_DATA_SIZE]; + uint16_t datalen = 0; + uint8_t cmdc = 0; + if (strlen(cmd)<2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); PrintAndLog(" -s activate field and select card"); @@ -563,7 +567,42 @@ int CmdHF14AAPDU(const char *cmd) { return 0; } + cmdc |= ISO14A_CONNECT; + cmdc |= ISO14A_NO_DISCONNECT; + UsbCommand c = {CMD_READER_ISO_14443a, {cmdc | ISO14A_APDU | ISO14A_SET_TIMEOUT, 0, 100}}; // 100-timeout in iso14a_set_timeout() + // Max buffer is USB_CMD_DATA_SIZE (512) + c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)numbits << 16); + + uint8_t first, second; + ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + data[datalen++] = first; + data[datalen++] = second; + + memcpy(c.d.asBytes,data,datalen); + + SendCommand(&c); + + if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + recv = resp.d.asBytes; + uint8_t iLen = resp.arg[0]; + if(!iLen) + return; + hexout = (char *)malloc(iLen * 3 + 1); + if (hexout != NULL) { + for (int i = 0; i < iLen; i++) { // data in hex + sprintf(&hexout[i * 3], "%02X ", recv[i]); + } + PrintAndLog("%s", hexout); + free(hexout); + } else { + PrintAndLog("malloc failed..."); + return 1; + } + } else { + PrintAndLog("timeout while waiting for reply."); + return 2; + } return 0; } From 980417eacd9a054b999ffdd3f83008f543b80c41 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 30 Oct 2017 17:29:03 +0200 Subject: [PATCH 03/23] iso14_apdu works via logical channel. not as it needs. it needs to change to raw( --- client/cmdhf14a.c | 114 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 18 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index d84a8f35..472f2fe4 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -558,6 +558,12 @@ int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; uint16_t datalen = 0; uint8_t cmdc = 0; + char buf[5] = {0}; + int i = 0; + uint32_t temp; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; if (strlen(cmd)<2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); @@ -566,42 +572,114 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog(" -t executes TLV decoder if it possible"); return 0; } - - cmdc |= ISO14A_CONNECT; - cmdc |= ISO14A_NO_DISCONNECT; - - UsbCommand c = {CMD_READER_ISO_14443a, {cmdc | ISO14A_APDU | ISO14A_SET_TIMEOUT, 0, 100}}; // 100-timeout in iso14a_set_timeout() - // Max buffer is USB_CMD_DATA_SIZE (512) - c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)numbits << 16); - uint8_t first, second; - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; + // strip + while (*cmd==' ' || *cmd=='\t') cmd++; - memcpy(c.d.asBytes,data,datalen); + while (cmd[i]!='\0') { + if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } + if (cmd[i]=='-') { + switch (cmd[i + 1]) { + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 't': + case 'T': + decodeTLV = true; + break; + default: + PrintAndLog("Invalid option"); + return 1; + } + i += 2; + continue; + } + if ((cmd[i] >= '0' && cmd[i] <= '9') || + (cmd[i] >= 'a' && cmd[i] <= 'f') || + (cmd[i] >= 'A' && cmd[i] <= 'F') ) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = cmd[i]; + i++; + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + if (datalen > sizeof(data) - 2) { + PrintAndLog("Buffer is full..."); + break; + } else { + datalen++; + } + } + continue; + } + PrintAndLog("Invalid char on input"); + return 1; + } + if (*buf) { + PrintAndLog("Hex must have even number of digits. Detected %d symbols.", datalen * 2 + strlen(buf)); + return 1; + } + + PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); + + if (activateField) + cmdc |= ISO14A_CONNECT; + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes + // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size + // here length USB_CMD_DATA_SIZE=512 + // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + +// uint8_t first, second; +// ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); +// data[datalen++] = first; +// data[datalen++] = second; + + memcpy(c.d.asBytes, data, datalen); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + uint8_t *recv; + char *hexout; + UsbCommand resp; + + if (activateField) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + return 2; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { recv = resp.d.asBytes; uint8_t iLen = resp.arg[0]; if(!iLen) - return; + return 2; hexout = (char *)malloc(iLen * 3 + 1); if (hexout != NULL) { for (int i = 0; i < iLen; i++) { // data in hex sprintf(&hexout[i * 3], "%02X ", recv[i]); } - PrintAndLog("%s", hexout); + PrintAndLog("<<<< %s", hexout); + + // here TLV decoder... + free(hexout); } else { PrintAndLog("malloc failed..."); - return 1; + return 2; } } else { - PrintAndLog("timeout while waiting for reply."); - return 2; + PrintAndLog("Reply timeout."); + return 3; } return 0; From f1a983a330c983e2858c5b97012c949b243a4db1 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 30 Oct 2017 17:58:43 +0200 Subject: [PATCH 04/23] fixed bug in CmdHF14ACmdRaw: if we cant select we send command anyway... --- armsrc/iso14443a.c | 18 ++++++++++++++++-- client/cmdhf14a.c | 19 ++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 026b177a..2ff722b0 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1910,6 +1910,7 @@ void ReaderIso14443a(UsbCommand *c) uint32_t arg0 = 0; byte_t buf[USB_CMD_DATA_SIZE]; uint8_t par[MAX_PARITY_SIZE]; + bool cantSELECT = false; if(param & ISO14A_CONNECT) { clear_trace(); @@ -1922,11 +1923,19 @@ void ReaderIso14443a(UsbCommand *c) } if(param & ISO14A_CONNECT) { + LED_A_ON(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); + + // if we cant select then we cant send data + cantSELECT = (arg0 != 1); + + LED_B_ON(); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); + LED_B_OFF(); } } @@ -1934,12 +1943,14 @@ void ReaderIso14443a(UsbCommand *c) iso14a_set_timeout(timeout); } - if(param & ISO14A_APDU) { + if(param & ISO14A_APDU && !cantSELECT) { arg0 = iso14_apdu(cmd, len, buf); + LED_B_ON(); cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + LED_B_OFF(); } - if(param & ISO14A_RAW) { + if(param & ISO14A_RAW && !cantSELECT) { if(param & ISO14A_APPEND_CRC) { if(param & ISO14A_TOPAZMODE) { AppendCrc14443b(cmd,len); @@ -1975,7 +1986,10 @@ void ReaderIso14443a(UsbCommand *c) } } arg0 = ReaderReceive(buf, par); + + LED_B_ON(); cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + LED_B_OFF(); } if(param & ISO14A_REQUEST_TRIGGER) { diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 472f2fe4..94c3ad2c 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -29,7 +29,7 @@ #include "mifarehost.h" static int CmdHelp(const char *Cmd); -static void waitCmd(uint8_t iLen); +static int waitCmd(uint8_t iLen); // structure and database for uid -> tagtype lookups typedef struct { @@ -656,6 +656,8 @@ int CmdHF14AAPDU(const char *cmd) { if (activateField) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return 2; + if (resp.arg[0] != 1) + return 2; } if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { @@ -845,17 +847,17 @@ int CmdHF14ACmdRaw(const char *cmd) { SendCommand(&c); if (reply) { - if(active_select) - waitCmd(1); - if(datalen>0) + int res = 0; + if (active_select) + res = waitCmd(1); + if (!res && datalen > 0) waitCmd(0); } // if reply return 0; } -static void waitCmd(uint8_t iSelect) -{ +static int waitCmd(uint8_t iSelect) { uint8_t *recv; UsbCommand resp; char *hexout; @@ -865,7 +867,7 @@ static void waitCmd(uint8_t iSelect) uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; PrintAndLog("received %i octets", iLen); if(!iLen) - return; + return 1; hexout = (char *)malloc(iLen * 3 + 1); if (hexout != NULL) { for (int i = 0; i < iLen; i++) { // data in hex @@ -875,10 +877,13 @@ static void waitCmd(uint8_t iSelect) free(hexout); } else { PrintAndLog("malloc failed your client has low memory?"); + return 2; } } else { PrintAndLog("timeout while waiting for reply."); + return 3; } + return 0; } static command_t CommandTable[] = From f2b0169cc69c9bb963c58d419bb8e313dcbe5b24 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 13:28:16 +0200 Subject: [PATCH 05/23] apdu works. --- client/cmdhf14a.c | 68 +++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 94c3ad2c..c16a64bd 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -561,6 +561,7 @@ int CmdHF14AAPDU(const char *cmd) { char buf[5] = {0}; int i = 0; uint32_t temp; + uint8_t first, second; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; @@ -627,30 +628,31 @@ int CmdHF14AAPDU(const char *cmd) { return 1; } - PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - if (activateField) cmdc |= ISO14A_CONNECT; if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + memmove(data + 1, data, datalen); + data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + datalen++; + + ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + data[datalen++] = first; + data[datalen++] = second; + + PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length USB_CMD_DATA_SIZE=512 // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; - -// uint8_t first, second; -// ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); -// data[datalen++] = first; -// data[datalen++] = second; - + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; memcpy(c.d.asBytes, data, datalen); - SendCommand(&c); uint8_t *recv; - char *hexout; UsbCommand resp; if (activateField) { @@ -665,22 +667,36 @@ int CmdHF14AAPDU(const char *cmd) { uint8_t iLen = resp.arg[0]; if(!iLen) return 2; - hexout = (char *)malloc(iLen * 3 + 1); - if (hexout != NULL) { - for (int i = 0; i < iLen; i++) { // data in hex - sprintf(&hexout[i * 3], "%02X ", recv[i]); - } - PrintAndLog("<<<< %s", hexout); - - // here TLV decoder... - - free(hexout); - } else { - PrintAndLog("malloc failed..."); - return 2; - } + + PrintAndLog("<<<< %s", sprint_hex(recv, iLen)); + + // check apdu length + if (iLen < 5) { + PrintAndLog("ERROR: Small APDU response."); + return 3; + } + + // check block + if (data[0] != recv[0]) { + PrintAndLog("ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); + return 3; + } + + // CRC Check + ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); + if (first || second) { + PrintAndLog("ERROR: ISO 14443A CRC error."); + return 3; + } + + PrintAndLog("APDU response: %02x %02x", recv[iLen - 4], recv[iLen - 3]); // TODO add APDU descriptions + + // here TLV decoder... + if (decodeTLV) { + } + } else { - PrintAndLog("Reply timeout."); + PrintAndLog("ERROR: Reply timeout."); return 3; } From 8019540b19fe60be30a4a003727260892934a242 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 15:15:57 +0200 Subject: [PATCH 06/23] param parsing convert to procedures --- client/cmdhf14a.c | 65 ++++++++++++++++++----------------------------- client/util.c | 57 ++++++++++++++++++++++++++++++++++++++--- client/util.h | 2 ++ 3 files changed, 81 insertions(+), 43 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index c16a64bd..336cb0d2 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -556,17 +556,14 @@ int CmdHF14ASnoop(const char *Cmd) { int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; - uint16_t datalen = 0; + int datalen = 0; uint8_t cmdc = 0; - char buf[5] = {0}; - int i = 0; - uint32_t temp; uint8_t first, second; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; - if (strlen(cmd)<2) { + if (strlen(cmd) < 2) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); PrintAndLog(" -s activate field and select card"); PrintAndLog(" -k leave the signal field ON after receive response"); @@ -574,13 +571,11 @@ int CmdHF14AAPDU(const char *cmd) { return 0; } - // strip - while (*cmd==' ' || *cmd=='\t') cmd++; - - while (cmd[i]!='\0') { - if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } - if (cmd[i]=='-') { - switch (cmd[i + 1]) { + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { case 's': case 'S': activateField = true; @@ -594,39 +589,29 @@ int CmdHF14AAPDU(const char *cmd) { decodeTLV = true; break; default: - PrintAndLog("Invalid option"); + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; } - i += 2; - continue; - } - if ((cmd[i] >= '0' && cmd[i] <= '9') || - (cmd[i] >= 'a' && cmd[i] <= 'f') || - (cmd[i] >= 'A' && cmd[i] <= 'F') ) { - buf[strlen(buf) + 1] = 0x00; - buf[strlen(buf)] = cmd[i]; - i++; - - if (strlen(buf) >= 2) { - sscanf(buf, "%x", &temp); - data[datalen] = (uint8_t)(temp & 0xff); - *buf = 0; - if (datalen > sizeof(data) - 2) { - PrintAndLog("Buffer is full..."); - break; - } else { - datalen++; - } + + if (isxdigit(c)) { + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("APDU too large."); + return 1; + case 3: + PrintAndLog("Hex must have even number of digits."); + return 1; } - continue; + + // we get all the hex to end of line with spaces + break; } - PrintAndLog("Invalid char on input"); - return 1; + + cmdp++; } - if (*buf) { - PrintAndLog("Hex must have even number of digits. Detected %d symbols.", datalen * 2 + strlen(buf)); - return 1; - } if (activateField) cmdc |= ISO14A_CONNECT; diff --git a/client/util.c b/client/util.c index 8357f601..9dab4655 100644 --- a/client/util.c +++ b/client/util.c @@ -364,13 +364,19 @@ int param_getlength(const char *line, int paramnum) return en - bg + 1; } -char param_getchar(const char *line, int paramnum) -{ +char param_getchar(const char *line, int paramnum) { + return param_getchar_indx(line, 0, paramnum); +} + +char param_getchar_indx(const char *line, int indx, int paramnum) { int bg, en; if (param_getptr(line, &bg, &en, paramnum)) return 0x00; - return line[bg]; + if (bg + indx > en) + return '\0'; + + return line[bg + indx]; } uint8_t param_get8(const char *line, int paramnum) @@ -480,6 +486,51 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt) return 0; } + +int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen) { + int bg, en; + uint32_t temp; + char buf[5] = {0}; + + if (param_getptr(line, &bg, &en, paramnum)) return 1; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') + continue; + + if (isxdigit(line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + int param_getstr(const char *line, int paramnum, char * str) { int bg, en; diff --git a/client/util.h b/client/util.h index d6ed7d17..f42625b1 100644 --- a/client/util.h +++ b/client/util.h @@ -53,6 +53,7 @@ extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t b extern int param_getlength(const char *line, int paramnum); extern char param_getchar(const char *line, int paramnum); +extern char param_getchar_indx(const char *line, int indx, int paramnum); extern int param_getptr(const char *line, int *bg, int *en, int paramnum); extern uint8_t param_get8(const char *line, int paramnum); extern uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base); @@ -62,6 +63,7 @@ extern uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination extern uint8_t param_isdec(const char *line, int paramnum); extern int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt); extern int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt); +extern int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen); extern int param_getstr(const char *line, int paramnum, char * str); extern int hextobinarray( char *target, char *source); From d1300b47ae47db3fef504d414cd0e86729234f85 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 16:08:58 +0200 Subject: [PATCH 07/23] apdu sending refactoring --- client/cmdhf14a.c | 167 +++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 68 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 336cb0d2..97a57c5f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -554,11 +554,86 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { + uint8_t data[USB_CMD_DATA_SIZE]; + int datalen; + uint8_t cmdc = 0; + uint8_t first, second; + + if (activateField) + cmdc |= ISO14A_CONNECT; + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + memcpy(data + 1, datain, datainlen); + data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + datalen = datainlen + 1; + + ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + data[datalen++] = first; + data[datalen++] = second; + + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes + // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size + // here length USB_CMD_DATA_SIZE=512 + // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + memcpy(c.d.asBytes, data, datalen); + SendCommand(&c); + + uint8_t *recv; + UsbCommand resp; + + if (activateField) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + return 1; + if (resp.arg[0] != 1) + return 1; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + recv = resp.d.asBytes; + uint8_t iLen = resp.arg[0]; + + *dataoutlen = iLen - 1 - 2; + if (*dataoutlen < 0) + *dataoutlen = 0; + memcpy(dataout, recv + 1, *dataoutlen); + + if(!iLen) + return 1; + + // check apdu length + if (iLen < 5) { + PrintAndLog("APDU ERROR: Small APDU response."); + return 2; + } + + // check block + if (data[0] != recv[0]) { + PrintAndLog("APDU ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); + return 2; + } + + // CRC Check + ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); + if (first || second) { + PrintAndLog("APDU ERROR: ISO 14443A CRC error."); + return 3; + } + + } else { + PrintAndLog("APDU ERROR: Reply timeout."); + return 4; + } + + return 0; +} + int CmdHF14AAPDU(const char *cmd) { uint8_t data[USB_CMD_DATA_SIZE]; int datalen = 0; - uint8_t cmdc = 0; - uint8_t first, second; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; @@ -594,7 +669,8 @@ int CmdHF14AAPDU(const char *cmd) { } if (isxdigit(c)) { - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { + // len = data + PCB(1b) + CRC(2b) + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) { case 1: PrintAndLog("Invalid HEX value."); return 1; @@ -612,78 +688,33 @@ int CmdHF14AAPDU(const char *cmd) { cmdp++; } - - if (activateField) - cmdc |= ISO14A_CONNECT; - if (leaveSignalON) - cmdc |= ISO14A_NO_DISCONNECT; - - // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 - memmove(data + 1, data, datalen); - data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) - datalen++; - - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes - // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size - // here length USB_CMD_DATA_SIZE=512 - // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; - memcpy(c.d.asBytes, data, datalen); - SendCommand(&c); - - uint8_t *recv; - UsbCommand resp; - - if (activateField) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) - return 2; - if (resp.arg[0] != 1) + switch(ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen)) { + case 0: + break; + case 1: + PrintAndLog("APDU ERROR: Send APDU error."); + return 1; + case 2: return 2; + case 3: + return 3; + case 4: + return 4; + default: + return 5; } - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - recv = resp.d.asBytes; - uint8_t iLen = resp.arg[0]; - if(!iLen) - return 2; + PrintAndLog("<<<< %s", sprint_hex(data, datalen)); + + PrintAndLog("APDU response: %02x %02x", data[datalen - 2], data[datalen - 1]); // TODO add APDU descriptions - PrintAndLog("<<<< %s", sprint_hex(recv, iLen)); - - // check apdu length - if (iLen < 5) { - PrintAndLog("ERROR: Small APDU response."); - return 3; - } - - // check block - if (data[0] != recv[0]) { - PrintAndLog("ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); - return 3; - } - - // CRC Check - ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); - if (first || second) { - PrintAndLog("ERROR: ISO 14443A CRC error."); - return 3; - } - - PrintAndLog("APDU response: %02x %02x", recv[iLen - 4], recv[iLen - 3]); // TODO add APDU descriptions - - // here TLV decoder... - if (decodeTLV) { - } - - } else { - PrintAndLog("ERROR: Reply timeout."); - return 3; - } + // here TLV decoder... + if (decodeTLV) { + PrintAndLog("--- TLV decoded:"); + } return 0; } From 6fc6cd0f57c9f036e3d1e976607df87dbb0214b2 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 16:20:23 +0200 Subject: [PATCH 08/23] export apdu function --- client/cmdhf14a.c | 17 ----------------- client/cmdhf14a.h | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 97a57c5f..cdaa18cd 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -9,24 +9,7 @@ // High frequency ISO14443A commands //----------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include "util.h" -#include "util_posix.h" -#include "iso14443crc.h" -#include "data.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhf14a.h" -#include "common.h" -#include "cmdmain.h" -#include "mifare.h" -#include "cmdhfmfu.h" -#include "mifarehost.h" static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index dfdf1f4a..0b58a55a 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -12,7 +12,24 @@ #ifndef CMDHF14A_H__ #define CMDHF14A_H__ +#include #include +#include +#include +#include +#include +#include "util.h" +#include "util_posix.h" +#include "iso14443crc.h" +#include "data.h" +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "cmdmain.h" +#include "mifare.h" +#include "cmdhfmfu.h" +#include "mifarehost.h" int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); @@ -22,4 +39,6 @@ int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); +extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen); + #endif From a23f000182dfde6f2b429c15ff8a7cff87c8a40f Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 16:25:42 +0200 Subject: [PATCH 09/23] move strucrture to .h file --- client/cmdhf14a.c | 7 ------- client/cmdhf14a.h | 6 ++++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index cdaa18cd..f6ed899a 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -14,12 +14,6 @@ static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); -// structure and database for uid -> tagtype lookups -typedef struct { - uint8_t uid; - char* desc; -} manufactureName; - const manufactureName manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, @@ -93,7 +87,6 @@ const manufactureName manufactureMapping[] = { { 0x00, "no tag-info available" } // must be the last entry }; - // get a product description based on the UID // uid[8] tag uid // returns description of the best match diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 0b58a55a..407cf07d 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,12 @@ #include "cmdhfmfu.h" #include "mifarehost.h" +// structure and database for uid -> tagtype lookups +typedef struct { + uint8_t uid; + char* desc; +} manufactureName; + int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); int CmdHF14AMifare(const char *Cmd); From fe346768b35409677b6c7f40a6f2a6768cf54ac3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 18:14:32 +0200 Subject: [PATCH 10/23] added showing apdu response codes --- client/Makefile | 1 + client/cmdhf14a.c | 4 +- client/cmdhf14a.h | 1 + client/emv/apduinfo.c | 304 ++++++++++++++++++++++++++++++++++++++++++ client/emv/apduinfo.h | 34 +++++ client/obj/emv/.dummy | 0 6 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 client/emv/apduinfo.c create mode 100644 client/emv/apduinfo.h create mode 100644 client/obj/emv/.dummy diff --git a/client/Makefile b/client/Makefile index 3a96e9e9..8e1574d4 100644 --- a/client/Makefile +++ b/client/Makefile @@ -107,6 +107,7 @@ CMDSRCS = crapto1/crapto1.c\ ui.c \ cmddata.c \ lfdemod.c \ + emv/apduinfo.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index f6ed899a..6d6ea854 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2011, Merlok +// 2011, 2017 Merlok // Copyright (C) 2010 iZsh , Hagen Fritsch // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -685,7 +685,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("<<<< %s", sprint_hex(data, datalen)); - PrintAndLog("APDU response: %02x %02x", data[datalen - 2], data[datalen - 1]); // TODO add APDU descriptions + PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); // here TLV decoder... if (decodeTLV) { diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 407cf07d..919ece5a 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -30,6 +30,7 @@ #include "mifare.h" #include "cmdhfmfu.h" #include "mifarehost.h" +#include "emv/apduinfo.h" // structure and database for uid -> tagtype lookups typedef struct { diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c new file mode 100644 index 00000000..e2e51d9d --- /dev/null +++ b/client/emv/apduinfo.c @@ -0,0 +1,304 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#include "apduinfo.h" + +const APDUCode APDUCodeTable[] = { + // ID Type Description + {"XXXX", APDUCODE_TYPE_NONE, ""}, // blank string + {"6---", APDUCODE_TYPE_ERROR, "Class not supported."}, + {"61--", APDUCODE_TYPE_INFO, "Response bytes still available"}, + {"61XX", APDUCODE_TYPE_INFO, "Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE."}, + {"62--", APDUCODE_TYPE_WARNING, "State of non-volatile memory unchanged"}, + {"6200", APDUCODE_TYPE_WARNING, "No information given (NV-Ram not changed)"}, + {"6201", APDUCODE_TYPE_WARNING, "NV-Ram not changed 1."}, + {"6281", APDUCODE_TYPE_WARNING, "Part of returned data may be corrupted"}, + {"6282", APDUCODE_TYPE_WARNING, "End of file/record reached before reading Le bytes"}, + {"6283", APDUCODE_TYPE_WARNING, "Selected file invalidated"}, + {"6284", APDUCODE_TYPE_WARNING, "Selected file is not valid. FCI not formated according to ISO"}, + {"6285", APDUCODE_TYPE_WARNING, "No input data available from a sensor on the card. No Purse Engine enslaved for R3bc"}, + {"62A2", APDUCODE_TYPE_WARNING, "Wrong R-MAC"}, + {"62A4", APDUCODE_TYPE_WARNING, "Card locked (during reset( ))"}, + {"62CX", APDUCODE_TYPE_WARNING, "Counter with value x (command dependent)"}, + {"62F1", APDUCODE_TYPE_WARNING, "Wrong C-MAC"}, + {"62F3", APDUCODE_TYPE_WARNING, "Internal reset"}, + {"62F5", APDUCODE_TYPE_WARNING, "Default agent locked"}, + {"62F7", APDUCODE_TYPE_WARNING, "Cardholder locked"}, + {"62F8", APDUCODE_TYPE_WARNING, "Basement is current agent"}, + {"62F9", APDUCODE_TYPE_WARNING, "CALC Key Set not unblocked"}, + {"62FX", APDUCODE_TYPE_WARNING, "-"}, + {"62XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"63--", APDUCODE_TYPE_WARNING, "State of non-volatile memory changed"}, + {"6300", APDUCODE_TYPE_WARNING, "No information given (NV-Ram changed)"}, + {"6381", APDUCODE_TYPE_WARNING, "File filled up by the last write. Loading/updating is not allowed."}, + {"6382", APDUCODE_TYPE_WARNING, "Card key not supported."}, + {"6383", APDUCODE_TYPE_WARNING, "Reader key not supported."}, + {"6384", APDUCODE_TYPE_WARNING, "Plaintext transmission not supported."}, + {"6385", APDUCODE_TYPE_WARNING, "Secured transmission not supported."}, + {"6386", APDUCODE_TYPE_WARNING, "Volatile memory is not available."}, + {"6387", APDUCODE_TYPE_WARNING, "Non-volatile memory is not available."}, + {"6388", APDUCODE_TYPE_WARNING, "Key number not valid."}, + {"6389", APDUCODE_TYPE_WARNING, "Key length is not correct."}, + {"63C0", APDUCODE_TYPE_WARNING, "Verify fail, no try left."}, + {"63C1", APDUCODE_TYPE_WARNING, "Verify fail, 1 try left."}, + {"63C2", APDUCODE_TYPE_WARNING, "Verify fail, 2 tries left."}, + {"63C3", APDUCODE_TYPE_WARNING, "Verify fail, 3 tries left."}, + {"63CX", APDUCODE_TYPE_WARNING, "The counter has reached the value 'x' (0 = x = 15) (command dependent)."}, + {"63F1", APDUCODE_TYPE_WARNING, "More data expected."}, + {"63F2", APDUCODE_TYPE_WARNING, "More data expected and proactive command pending."}, + {"63FX", APDUCODE_TYPE_WARNING, "-"}, + {"63XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"64--", APDUCODE_TYPE_ERROR, "State of non-volatile memory unchanged"}, + {"6400", APDUCODE_TYPE_ERROR, "No information given (NV-Ram not changed)"}, + {"6401", APDUCODE_TYPE_ERROR, "Command timeout. Immediate response required by the card."}, + {"64XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"65--", APDUCODE_TYPE_ERROR, "State of non-volatile memory changed"}, + {"6500", APDUCODE_TYPE_ERROR, "No information given"}, + {"6501", APDUCODE_TYPE_ERROR, "Write error. Memory failure. There have been problems in writing or reading the EEPROM. Other hardware problems may also bring this error."}, + {"6581", APDUCODE_TYPE_ERROR, "Memory failure"}, + {"65FX", APDUCODE_TYPE_ERROR, "-"}, + {"65XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"66--", APDUCODE_TYPE_SECURITY, " "}, + {"6600", APDUCODE_TYPE_SECURITY, "Error while receiving (timeout)"}, + {"6601", APDUCODE_TYPE_SECURITY, "Error while receiving (character parity error)"}, + {"6602", APDUCODE_TYPE_SECURITY, "Wrong checksum"}, + {"6603", APDUCODE_TYPE_SECURITY, "The current DF file without FCI"}, + {"6604", APDUCODE_TYPE_SECURITY, "No SF or KF under the current DF"}, + {"6669", APDUCODE_TYPE_SECURITY, "Incorrect Encryption/Decryption Padding"}, + {"66XX", APDUCODE_TYPE_SECURITY, "-"}, + {"67--", APDUCODE_TYPE_ERROR, " "}, + {"6700", APDUCODE_TYPE_ERROR, "Wrong length"}, + {"67XX", APDUCODE_TYPE_ERROR, "length incorrect (procedure)(ISO 7816-3)"}, + {"68--", APDUCODE_TYPE_ERROR, "Functions in CLA not supported"}, + {"6800", APDUCODE_TYPE_ERROR, "No information given (The request function is not supported by the card)"}, + {"6881", APDUCODE_TYPE_ERROR, "Logical channel not supported"}, + {"6882", APDUCODE_TYPE_ERROR, "Secure messaging not supported"}, + {"6883", APDUCODE_TYPE_ERROR, "Last command of the chain expected"}, + {"6884", APDUCODE_TYPE_ERROR, "Command chaining not supported"}, + {"68FX", APDUCODE_TYPE_ERROR, "-"}, + {"68XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"69--", APDUCODE_TYPE_ERROR, "Command not allowed"}, + {"6900", APDUCODE_TYPE_ERROR, "No information given (Command not allowed)"}, + {"6901", APDUCODE_TYPE_ERROR, "Command not accepted (inactive state)"}, + {"6981", APDUCODE_TYPE_ERROR, "Command incompatible with file structure"}, + {"6982", APDUCODE_TYPE_ERROR, "Security condition not satisfied."}, + {"6983", APDUCODE_TYPE_ERROR, "Authentication method blocked"}, + {"6984", APDUCODE_TYPE_ERROR, "Referenced data reversibly blocked (invalidated)"}, + {"6985", APDUCODE_TYPE_ERROR, "Conditions of use not satisfied."}, + {"6986", APDUCODE_TYPE_ERROR, "Command not allowed (no current EF)"}, + {"6987", APDUCODE_TYPE_ERROR, "Expected secure messaging (SM) object missing"}, + {"6988", APDUCODE_TYPE_ERROR, "Incorrect secure messaging (SM) data object"}, + {"698D", APDUCODE_TYPE_NONE, "Reserved"}, + {"6996", APDUCODE_TYPE_ERROR, "Data must be updated again"}, + {"69E1", APDUCODE_TYPE_ERROR, "POL1 of the currently Enabled Profile prevents this action."}, + {"69F0", APDUCODE_TYPE_ERROR, "Permission Denied"}, + {"69F1", APDUCODE_TYPE_ERROR, "Permission Denied - Missing Privilege"}, + {"69FX", APDUCODE_TYPE_ERROR, "-"}, + {"69XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6A--", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6A00", APDUCODE_TYPE_ERROR, "No information given (Bytes P1 and/or P2 are incorrect)"}, + {"6A80", APDUCODE_TYPE_ERROR, "The parameters in the data field are incorrect."}, + {"6A81", APDUCODE_TYPE_ERROR, "Function not supported"}, + {"6A82", APDUCODE_TYPE_ERROR, "File not found"}, + {"6A83", APDUCODE_TYPE_ERROR, "Record not found"}, + {"6A84", APDUCODE_TYPE_ERROR, "There is insufficient memory space in record or file"}, + {"6A85", APDUCODE_TYPE_ERROR, "Lc inconsistent with TLV structure"}, + {"6A86", APDUCODE_TYPE_ERROR, "Incorrect P1 or P2 parameter."}, + {"6A87", APDUCODE_TYPE_ERROR, "Lc inconsistent with P1-P2"}, + {"6A88", APDUCODE_TYPE_ERROR, "Referenced data not found"}, + {"6A89", APDUCODE_TYPE_ERROR, "File already exists"}, + {"6A8A", APDUCODE_TYPE_ERROR, "DF name already exists."}, + {"6AF0", APDUCODE_TYPE_ERROR, "Wrong parameter value"}, + {"6AFX", APDUCODE_TYPE_ERROR, "-"}, + {"6AXX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6B--", APDUCODE_TYPE_ERROR, " "}, + {"6B00", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6BXX", APDUCODE_TYPE_ERROR, "Reference incorrect (procedure byte), (ISO 7816-3)"}, + {"6C--", APDUCODE_TYPE_ERROR, "Wrong length Le"}, + {"6C00", APDUCODE_TYPE_ERROR, "Incorrect P3 length."}, + {"6CXX", APDUCODE_TYPE_ERROR, "Bad length value in Le; 'xx' is the correct exact Le"}, + {"6D--", APDUCODE_TYPE_ERROR, " "}, + {"6D00", APDUCODE_TYPE_ERROR, "Instruction code not supported or invalid"}, + {"6DXX", APDUCODE_TYPE_ERROR, "Instruction code not programmed or invalid (procedure byte), (ISO 7816-3)"}, + {"6E--", APDUCODE_TYPE_ERROR, " "}, + {"6E00", APDUCODE_TYPE_ERROR, "Class not supported"}, + {"6EXX", APDUCODE_TYPE_ERROR, "Instruction class not supported (procedure byte), (ISO 7816-3)"}, + {"6F--", APDUCODE_TYPE_ERROR, "Internal exception"}, + {"6F00", APDUCODE_TYPE_ERROR, "Command aborted - more exact diagnosis not possible (e.g., operating system error)."}, + {"6FFF", APDUCODE_TYPE_ERROR, "Card dead (overuse, …)"}, + {"6FXX", APDUCODE_TYPE_ERROR, "No precise diagnosis (procedure byte), (ISO 7816-3)"}, + {"9---", APDUCODE_TYPE_NONE, ""}, + {"9000", APDUCODE_TYPE_INFO, "Command successfully executed (OK)."}, + {"9004", APDUCODE_TYPE_WARNING, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9008", APDUCODE_TYPE_NONE, "Key/file not found"}, + {"9080", APDUCODE_TYPE_WARNING, "Unblock Try Counter has reached zero"}, + {"9100", APDUCODE_TYPE_NONE, "OK"}, + {"9101", APDUCODE_TYPE_NONE, "States.activity, States.lock Status or States.lockable has wrong value"}, + {"9102", APDUCODE_TYPE_NONE, "Transaction number reached its limit"}, + {"910C", APDUCODE_TYPE_NONE, "No changes"}, + {"910E", APDUCODE_TYPE_NONE, "Insufficient NV-Memory to complete command"}, + {"911C", APDUCODE_TYPE_NONE, "Command code not supported"}, + {"911E", APDUCODE_TYPE_NONE, "CRC or MAC does not match data"}, + {"9140", APDUCODE_TYPE_NONE, "Invalid key number specified"}, + {"917E", APDUCODE_TYPE_NONE, "Length of command string invalid"}, + {"919D", APDUCODE_TYPE_NONE, "Not allow the requested command"}, + {"919E", APDUCODE_TYPE_NONE, "Value of the parameter invalid"}, + {"91A0", APDUCODE_TYPE_NONE, "Requested AID not present on PICC"}, + {"91A1", APDUCODE_TYPE_NONE, "Unrecoverable error within application"}, + {"91AE", APDUCODE_TYPE_NONE, "Authentication status does not allow the requested command"}, + {"91AF", APDUCODE_TYPE_NONE, "Additional data frame is expected to be sent"}, + {"91BE", APDUCODE_TYPE_NONE, "Out of boundary"}, + {"91C1", APDUCODE_TYPE_NONE, "Unrecoverable error within PICC"}, + {"91CA", APDUCODE_TYPE_NONE, "Previous Command was not fully completed"}, + {"91CD", APDUCODE_TYPE_NONE, "PICC was disabled by an unrecoverable error"}, + {"91CE", APDUCODE_TYPE_NONE, "Number of Applications limited to 28"}, + {"91DE", APDUCODE_TYPE_NONE, "File or application already exists"}, + {"91EE", APDUCODE_TYPE_NONE, "Could not complete NV-write operation due to loss of power"}, + {"91F0", APDUCODE_TYPE_NONE, "Specified file number does not exist"}, + {"91F1", APDUCODE_TYPE_NONE, "Unrecoverable error within file"}, + {"920x", APDUCODE_TYPE_INFO, "Writing to EEPROM successful after 'x' attempts."}, + {"9210", APDUCODE_TYPE_ERROR, "Insufficient memory. No more storage available."}, + {"9240", APDUCODE_TYPE_ERROR, "Writing to EEPROM not successful."}, + {"9301", APDUCODE_TYPE_NONE, "Integrity error"}, + {"9302", APDUCODE_TYPE_NONE, "Candidate S2 invalid"}, + {"9303", APDUCODE_TYPE_ERROR, "Application is permanently locked"}, + {"9400", APDUCODE_TYPE_ERROR, "No EF selected."}, + {"9401", APDUCODE_TYPE_NONE, "Candidate currency code does not match purse currency"}, + {"9402", APDUCODE_TYPE_NONE, "Candidate amount too high"}, + {"9402", APDUCODE_TYPE_ERROR, "Address range exceeded."}, + {"9403", APDUCODE_TYPE_NONE, "Candidate amount too low"}, + {"9404", APDUCODE_TYPE_ERROR, "FID not found, record not found or comparison pattern not found."}, + {"9405", APDUCODE_TYPE_NONE, "Problems in the data field"}, + {"9406", APDUCODE_TYPE_ERROR, "Required MAC unavailable"}, + {"9407", APDUCODE_TYPE_NONE, "Bad currency : purse engine has no slot with R3bc currency"}, + {"9408", APDUCODE_TYPE_NONE, "R3bc currency not supported in purse engine"}, + {"9408", APDUCODE_TYPE_ERROR, "Selected file type does not match command."}, + {"9580", APDUCODE_TYPE_NONE, "Bad sequence"}, + {"9681", APDUCODE_TYPE_NONE, "Slave not found"}, + {"9700", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 1 or 2"}, + {"9702", APDUCODE_TYPE_NONE, "Main keys are blocked"}, + {"9704", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9784", APDUCODE_TYPE_NONE, "Base key"}, + {"9785", APDUCODE_TYPE_NONE, "Limit exceeded - C-MAC key"}, + {"9786", APDUCODE_TYPE_NONE, "SM error - Limit exceeded - R-MAC key"}, + {"9787", APDUCODE_TYPE_NONE, "Limit exceeded - sequence counter"}, + {"9788", APDUCODE_TYPE_NONE, "Limit exceeded - R-MAC length"}, + {"9789", APDUCODE_TYPE_NONE, "Service not available"}, + {"9802", APDUCODE_TYPE_ERROR, "No PIN defined."}, + {"9804", APDUCODE_TYPE_ERROR, "Access conditions not satisfied, authentication failed."}, + {"9835", APDUCODE_TYPE_ERROR, "ASK RANDOM or GIVE RANDOM not executed."}, + {"9840", APDUCODE_TYPE_ERROR, "PIN verification not successful."}, + {"9850", APDUCODE_TYPE_ERROR, "INCREASE or DECREASE could not be executed because a limit has been reached."}, + {"9862", APDUCODE_TYPE_ERROR, "Authentication Error, application specific (incorrect MAC)"}, + {"9900", APDUCODE_TYPE_NONE, "1 PIN try left"}, + {"9904", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 1 PIN try left"}, + {"9985", APDUCODE_TYPE_NONE, "Wrong status - Cardholder lock"}, + {"9986", APDUCODE_TYPE_ERROR, "Missing privilege"}, + {"9987", APDUCODE_TYPE_NONE, "PIN is not installed"}, + {"9988", APDUCODE_TYPE_NONE, "Wrong status - R-MAC state"}, + {"9A00", APDUCODE_TYPE_NONE, "2 PIN try left"}, + {"9A04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 2 PIN try left"}, + {"9A71", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent AID"}, + {"9A72", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent Type"}, + {"9D05", APDUCODE_TYPE_ERROR, "Incorrect certificate type"}, + {"9D07", APDUCODE_TYPE_ERROR, "Incorrect session data size"}, + {"9D08", APDUCODE_TYPE_ERROR, "Incorrect DIR file record size"}, + {"9D09", APDUCODE_TYPE_ERROR, "Incorrect FCI record size"}, + {"9D0A", APDUCODE_TYPE_ERROR, "Incorrect code size"}, + {"9D10", APDUCODE_TYPE_ERROR, "Insufficient memory to load application"}, + {"9D11", APDUCODE_TYPE_ERROR, "Invalid AID"}, + {"9D12", APDUCODE_TYPE_ERROR, "Duplicate AID"}, + {"9D13", APDUCODE_TYPE_ERROR, "Application previously loaded"}, + {"9D14", APDUCODE_TYPE_ERROR, "Application history list full"}, + {"9D15", APDUCODE_TYPE_ERROR, "Application not open"}, + {"9D17", APDUCODE_TYPE_ERROR, "Invalid offset"}, + {"9D18", APDUCODE_TYPE_ERROR, "Application already loaded"}, + {"9D19", APDUCODE_TYPE_ERROR, "Invalid certificate"}, + {"9D1A", APDUCODE_TYPE_ERROR, "Invalid signature"}, + {"9D1B", APDUCODE_TYPE_ERROR, "Invalid KTU"}, + {"9D1D", APDUCODE_TYPE_ERROR, "MSM controls not set"}, + {"9D1E", APDUCODE_TYPE_ERROR, "Application signature does not exist"}, + {"9D1F", APDUCODE_TYPE_ERROR, "KTU does not exist"}, + {"9D20", APDUCODE_TYPE_ERROR, "Application not loaded"}, + {"9D21", APDUCODE_TYPE_ERROR, "Invalid Open command data length"}, + {"9D30", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid start address)"}, + {"9D31", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid length)"}, + {"9D32", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (illegal memory check area)"}, + {"9D40", APDUCODE_TYPE_ERROR, "Invalid MSM Controls ciphertext"}, + {"9D41", APDUCODE_TYPE_ERROR, "MSM controls already set"}, + {"9D42", APDUCODE_TYPE_ERROR, "Set MSM Controls data length less than 2 bytes"}, + {"9D43", APDUCODE_TYPE_ERROR, "Invalid MSM Controls data length"}, + {"9D44", APDUCODE_TYPE_ERROR, "Excess MSM Controls ciphertext"}, + {"9D45", APDUCODE_TYPE_ERROR, "Verification of MSM Controls data failed"}, + {"9D50", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer production ID"}, + {"9D51", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer ID"}, + {"9D52", APDUCODE_TYPE_ERROR, "Invalid set MSM controls data date"}, + {"9D53", APDUCODE_TYPE_ERROR, "Invalid MCD number"}, + {"9D54", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D55", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D56", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D57", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D60", APDUCODE_TYPE_ERROR, "MAC verification failed"}, + {"9D61", APDUCODE_TYPE_ERROR, "Maximum number of unblocks reached"}, + {"9D62", APDUCODE_TYPE_ERROR, "Card was not blocked"}, + {"9D63", APDUCODE_TYPE_ERROR, "Crypto functions not available"}, + {"9D64", APDUCODE_TYPE_ERROR, "No application loaded"}, + {"9E00", APDUCODE_TYPE_NONE, "PIN not installed"}, + {"9E04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN not installed"}, + {"9F00", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 3"}, + {"9F04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN blocked and Unblock Try Counter is 3"}, + {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, + {"9xXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} +}; + +int CodeCmp(const char *code1, const char *code2) { + int xsymb = 0; + int cmp = 0; + for (int i = 0; i < 4; i++) { + if (code1[i] == code2[i]) + cmp++; + if (code1[i] == 'X' || code2[i] == 'X') + xsymb++; + } + if (cmp == 4) + return 0; + + if (cmp + xsymb == 4) + return xsymb; + + return -1; +} + +APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) { + char buf[4] = {0}; + + sprintf(&buf[0], "%02X ", sw1); + sprintf(&buf[2], "%02X ", sw2); + + + int tableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); + for (int i = 0; i < tableLen; i++) { + if (CodeCmp(APDUCodeTable[i].ID, buf) == 0) { // TODO make not so equal comparation... XXXX - not works... + return &APDUCodeTable[i]; + } + } + + return NULL; +} + +const char *GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { + APDUCode *cd = GetAPDUCode(sw1, sw2); + if (cd) + return cd->Description; + else + return APDUCodeTable[0].Description; //empty string +} + + diff --git a/client/emv/apduinfo.h b/client/emv/apduinfo.h new file mode 100644 index 00000000..e388ae68 --- /dev/null +++ b/client/emv/apduinfo.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#ifndef APDUINFO_H__ +#define APDUINFO_H__ + +#include +#include +#include +#include + +#define APDUCODE_TYPE_NONE 0 +#define APDUCODE_TYPE_INFO 1 +#define APDUCODE_TYPE_WARNING 2 +#define APDUCODE_TYPE_ERROR 3 +#define APDUCODE_TYPE_SECURITY 4 + +typedef struct { + const char *ID; + const uint8_t Type; + const char *Description; +} APDUCode; + +extern APDUCode* GetAPDUCode(uint8_t sw1, uint8_t sw2); +extern const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2); + +#endif diff --git a/client/obj/emv/.dummy b/client/obj/emv/.dummy new file mode 100644 index 00000000..e69de29b From 5bcb3496fa0409fb7a1dd22e9357a812c3f3f4fe Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 19:11:23 +0200 Subject: [PATCH 11/23] small refactoring --- client/cmdhf14a.c | 2 +- client/emv/apduinfo.c | 13 +++++-------- client/emv/apduinfo.h | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 6d6ea854..aebe2463 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -618,7 +618,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("Usage: hf 14a apdu [-s] [-k] [-t] "); PrintAndLog(" -s activate field and select card"); PrintAndLog(" -k leave the signal field ON after receive response"); - PrintAndLog(" -t executes TLV decoder if it possible"); + PrintAndLog(" -t executes TLV decoder if it possible. TODO!!!!"); return 0; } diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index e2e51d9d..d73c24fc 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -257,6 +257,7 @@ const APDUCode APDUCodeTable[] = { {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, {"9xXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} }; +const size_t APDUCodeTableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); int CodeCmp(const char *code1, const char *code2) { int xsymb = 0; @@ -276,15 +277,13 @@ int CodeCmp(const char *code1, const char *code2) { return -1; } -APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) { +const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { char buf[4] = {0}; sprintf(&buf[0], "%02X ", sw1); sprintf(&buf[2], "%02X ", sw2); - - int tableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); - for (int i = 0; i < tableLen; i++) { + for (int i = 0; i < APDUCodeTableLen; i++) { if (CodeCmp(APDUCodeTable[i].ID, buf) == 0) { // TODO make not so equal comparation... XXXX - not works... return &APDUCodeTable[i]; } @@ -293,12 +292,10 @@ APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) { return NULL; } -const char *GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { - APDUCode *cd = GetAPDUCode(sw1, sw2); +const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { + const APDUCode *cd = GetAPDUCode(sw1, sw2); if (cd) return cd->Description; else return APDUCodeTable[0].Description; //empty string } - - diff --git a/client/emv/apduinfo.h b/client/emv/apduinfo.h index e388ae68..a3fa2049 100644 --- a/client/emv/apduinfo.h +++ b/client/emv/apduinfo.h @@ -28,7 +28,7 @@ typedef struct { const char *Description; } APDUCode; -extern APDUCode* GetAPDUCode(uint8_t sw1, uint8_t sw2); +extern const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2); extern const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2); #endif From 78a94ff9022f84d186d01e1baa0eafdf78a7652d Mon Sep 17 00:00:00 2001 From: merlokk Date: Tue, 31 Oct 2017 19:19:49 +0200 Subject: [PATCH 12/23] improved GetAPDUCode() --- client/emv/apduinfo.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c index d73c24fc..fbdd714a 100644 --- a/client/emv/apduinfo.c +++ b/client/emv/apduinfo.c @@ -279,16 +279,33 @@ int CodeCmp(const char *code1, const char *code2) { const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { char buf[4] = {0}; + int res; + int mineq = 100; + int mineqindx = 0; sprintf(&buf[0], "%02X ", sw1); sprintf(&buf[2], "%02X ", sw2); for (int i = 0; i < APDUCodeTableLen; i++) { - if (CodeCmp(APDUCodeTable[i].ID, buf) == 0) { // TODO make not so equal comparation... XXXX - not works... + res = CodeCmp(APDUCodeTable[i].ID, buf); + + // equal + if (res == 0) { return &APDUCodeTable[i]; } + + // with some 'X' + if (res > 0 && mineq > res) { + mineq = res; + mineqindx = i; + } } + // if we have not equal, but with some 'X' + if (mineqindx < 100) { + return &APDUCodeTable[mineqindx]; + } + return NULL; } From a2bb2735d5095aabeba4a891fe4ba867a9afc2b3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 13:38:29 +0200 Subject: [PATCH 13/23] TLV decoding works --- client/Makefile | 4 + client/cmdhf14a.c | 4 +- client/cmdhf14a.h | 1 + client/emv/dump.c | 57 +++++ client/emv/dump.h | 24 +++ client/emv/emv_tags.c | 480 ++++++++++++++++++++++++++++++++++++++++++ client/emv/emv_tags.h | 24 +++ client/emv/emvcore.c | 31 +++ client/emv/emvcore.h | 31 +++ client/emv/tlv.c | 415 ++++++++++++++++++++++++++++++++++++ client/emv/tlv.h | 52 +++++ 11 files changed, 1121 insertions(+), 2 deletions(-) create mode 100644 client/emv/dump.c create mode 100644 client/emv/dump.h create mode 100644 client/emv/emv_tags.c create mode 100644 client/emv/emv_tags.h create mode 100644 client/emv/emvcore.c create mode 100644 client/emv/emvcore.h create mode 100644 client/emv/tlv.c create mode 100644 client/emv/tlv.h diff --git a/client/Makefile b/client/Makefile index 8e1574d4..30e91efb 100644 --- a/client/Makefile +++ b/client/Makefile @@ -108,6 +108,10 @@ CMDSRCS = crapto1/crapto1.c\ cmddata.c \ lfdemod.c \ emv/apduinfo.c\ + emv/dump.c\ + emv/tlv.c\ + emv/emv_tags.c\ + emv/emvcore.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index aebe2463..1a7f6970 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -688,8 +688,8 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); // here TLV decoder... - if (decodeTLV) { - PrintAndLog("--- TLV decoded:"); + if (decodeTLV && datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); } return 0; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 919ece5a..d11d17eb 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,7 @@ #include "cmdhfmfu.h" #include "mifarehost.h" #include "emv/apduinfo.h" +#include "emv/emvcore.h" // structure and database for uid -> tagtype lookups typedef struct { diff --git a/client/emv/dump.c b/client/emv/dump.c new file mode 100644 index 00000000..9915ad73 --- /dev/null +++ b/client/emv/dump.c @@ -0,0 +1,57 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dump.h" + +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f) +{ + int i; + + if (!f) + f = stdout; + + for (i = 0; i < len; i ++) + fprintf(f, "%s%02hhX", i ? " " : "", ptr[i]); +} + +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f) +{ + int i, j; + + if (!f) + f = stdout; + + for (i = 0; i < len; i += 16) { + fprintf(f, "\t%02x:", i); + for (j = 0; j < 16; j++) { + if (i + j < len) + fprintf(f, " %02hhx", ptr[i + j]); + else + fprintf(f, " "); + } + fprintf(f, " |"); + for (j = 0; j < 16 && i + j < len; j++) { + fprintf(f, "%c", (ptr[i+j] >= 0x20 && ptr[i+j] < 0x7f) ? ptr[i+j] : '.' ); + } + fprintf(f, "\n"); + } +} + diff --git a/client/emv/dump.h b/client/emv/dump.h new file mode 100644 index 00000000..5976da44 --- /dev/null +++ b/client/emv/dump.h @@ -0,0 +1,24 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DUMP_H +#define DUMP_H + +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f); + +#endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c new file mode 100644 index 00000000..9bcf2004 --- /dev/null +++ b/client/emv/emv_tags.c @@ -0,0 +1,480 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" +#include "emv_tags.h" + +#include + +enum emv_tag_t { + EMV_TAG_GENERIC, + EMV_TAG_BITMASK, + EMV_TAG_DOL, + EMV_TAG_CVM_LIST, + EMV_TAG_STRING, + EMV_TAG_NUMERIC, + EMV_TAG_YYMMDD, + EMV_TAG_FCI, +}; + +struct emv_tag { + tlv_tag_t tag; + char *name; + enum emv_tag_t type; + const void *data; +}; + +struct emv_tag_bit { + unsigned bit; + const char *name; +}; + +#define EMV_BIT(byte, bit) ((byte - 1) * 8 + (8 - bit)) +#define EMV_BIT_FINISH { (~0), NULL } + +static const struct emv_tag_bit EMV_AIP[] = { + { EMV_BIT(1, 7), "SDA supported" }, + { EMV_BIT(1, 6), "DDA supported" }, + { EMV_BIT(1, 5), "Cardholder verification is supported" }, + { EMV_BIT(1, 4), "Terminal risk management is to be performed" }, + { EMV_BIT(1, 3), "Issuer authentication is supported" }, + { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(1, 1), "CDA supported" }, + { EMV_BIT(2, 8), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_AUC[] = { + { EMV_BIT(1, 8), "Valid for domestic cash transactions" }, + { EMV_BIT(1, 7), "Valid for international cash transactions" }, + { EMV_BIT(1, 6), "Valid for domestic goods" }, + { EMV_BIT(1, 5), "Valid for international goods" }, + { EMV_BIT(1, 4), "Valid for domestic services" }, + { EMV_BIT(1, 3), "Valid for international services" }, + { EMV_BIT(1, 2), "Valid for ATMs" }, + { EMV_BIT(1, 1), "Valid at terminals other than ATMs" }, + { EMV_BIT(2, 8), "Domestic cashback allowed" }, + { EMV_BIT(2, 7), "International cashback allowed" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TVR[] = { + { EMV_BIT(1, 8), "Offline data authentication was not performed" }, + { EMV_BIT(1, 7), "SDA failed" }, + { EMV_BIT(1, 6), "ICC data missing" }, + { EMV_BIT(1, 5), "Card appears on terminal exception file" }, + { EMV_BIT(1, 4), "DDA failed" }, + { EMV_BIT(1, 3), "CDA failed" }, + { EMV_BIT(1, 2), "SDA selected" }, + { EMV_BIT(2, 8), "ICC and terminal have different application versions" }, + { EMV_BIT(2, 7), "Expired application" }, + { EMV_BIT(2, 6), "Application not yet effective" }, + { EMV_BIT(2, 5), "Requested service not allowed for card product" }, + { EMV_BIT(2, 4), "New card" }, + { EMV_BIT(3, 8), "Cardholder verification was not successful" }, + { EMV_BIT(3, 7), "Unrecognised CVM" }, + { EMV_BIT(3, 6), "PIN Try Limit exceeded" }, + { EMV_BIT(3, 5), "PIN entry required and PIN pad not present or not working" }, + { EMV_BIT(3, 4), "PIN entry required, PIN pad present, but PIN was not entered" }, + { EMV_BIT(3, 3), "Online PIN entered" }, + { EMV_BIT(4, 8), "Transaction exceeds floor limit" }, + { EMV_BIT(4, 7), "Lower consecutive offline limit exceeded" }, + { EMV_BIT(4, 6), "Upper consecutive offline limit exceeded" }, + { EMV_BIT(4, 5), "Transaction selected randomly for online processing" }, + { EMV_BIT(4, 4), "Merchant forced transaction online" }, + { EMV_BIT(5, 8), "Default TDOL used" }, + { EMV_BIT(5, 7), "Issuer authentication failed" }, + { EMV_BIT(5, 6), "Script processing failed before final GENERATE AC" }, + { EMV_BIT(5, 5), "Script processing failed after final GENERATE AC" }, + { EMV_BIT(5, 4), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 3), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag emv_tags[] = { + { 0x00 , "Unknown ???" }, + { 0x4f , "Application Dedicated File (ADF) Name" }, + { 0x50 , "Application Label", EMV_TAG_STRING }, + { 0x56 , "Track 1 Data" }, + { 0x57 , "Track 2 Equivalent Data" }, + { 0x5a , "Application Primary Account Number (PAN)" }, + { 0x5f20, "Cardholder Name", EMV_TAG_STRING }, + { 0x5f24, "Application Expiration Date", EMV_TAG_YYMMDD }, + { 0x5f25, "Application Effective Date", EMV_TAG_YYMMDD }, + { 0x5f28, "Issuer Country Code", EMV_TAG_NUMERIC }, + { 0x5f2a, "Transaction Currency Code", EMV_TAG_NUMERIC }, + { 0x5f2d, "Language Preference", EMV_TAG_STRING }, + { 0x5f30, "Service Code", EMV_TAG_NUMERIC }, + { 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC }, + { 0x61 , "Application Template" }, + { 0x6f , "File Control Information (FCI) Template", EMV_TAG_FCI }, + { 0x70 , "READ RECORD Response Message Template" }, + { 0x77 , "Response Message Template Format 2" }, + { 0x80 , "Response Message Template Format 1" }, + { 0x82 , "Application Interchange Profile", EMV_TAG_BITMASK, &EMV_AIP }, + { 0x83 , "Command Template" }, + { 0x84 , "Dedicated File (DF) Name" }, + { 0x87 , "Application Priority Indicator" }, + { 0x88 , "Short File Identifier (SFI)" }, + { 0x8a , "Authorisation Response Code" }, + { 0x8c , "Card Risk Management Data Object List 1 (CDOL1)", EMV_TAG_DOL }, + { 0x8d , "Card Risk Management Data Object List 2 (CDOL2)", EMV_TAG_DOL }, + { 0x8e , "Cardholder Verification Method (CVM) List", EMV_TAG_CVM_LIST }, + { 0x8f , "Certification Authority Public Key Index" }, + { 0x90 , "Issuer Public Key Certificate" }, + { 0x91 , "Issuer Authentication Data" }, + { 0x92 , "Issuer Public Key Remainder" }, + { 0x93 , "Signed Static Application Data" }, + { 0x94 , "Application File Locator (AFL)" }, + { 0x95 , "Terminal Verification Results" }, + { 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, + { 0x9c , "Transaction Type" }, + { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, + { 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, }, + { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, + { 0x9f08, "Application Version Number" }, + { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0e, "Issuer Action Code - Denial", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0f, "Issuer Action Code - Online", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f10, "Issuer Application Data" }, + { 0x9f11, "Issuer Code Table Index", EMV_TAG_NUMERIC }, + { 0x9f12, "Application Preferred Name", EMV_TAG_STRING }, + { 0x9f13, "Last Online Application Transaction Counter (ATC) Register" }, + { 0x9f17, "Personal Identification Number (PIN) Try Counter" }, + { 0x9f1a, "Terminal Country Code" }, + { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING }, + { 0x9f21, "Transaction Time" }, + { 0x9f26, "Application Cryptogram" }, + { 0x9f27, "Cryptogram Information Data" }, + { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, + { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, + { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, + { 0x9f32, "Issuer Public Key Exponent" }, + { 0x9f34, "Cardholder Verification Method (CVM) Results" }, + { 0x9f35, "Terminal Type" }, + { 0x9f36, "Application Transaction Counter (ATC)" }, + { 0x9f37, "Unpredictable Number" }, + { 0x9f38, "Processing Options Data Object List (PDOL)", EMV_TAG_DOL }, + { 0x9f42, "Application Currency Code", EMV_TAG_NUMERIC }, + { 0x9f44, "Application Currency Exponent", EMV_TAG_NUMERIC }, + { 0x9f45, "Data Authentication Code" }, + { 0x9f46, "ICC Public Key Certificate" }, + { 0x9f47, "ICC Public Key Exponent" }, + { 0x9f48, "ICC Public Key Remainder" }, + { 0x9f49, "Dynamic Data Authentication Data Object List (DDOL)", EMV_TAG_DOL }, + { 0x9f4a, "Static Data Authentication Tag List" }, + { 0x9f4b, "Signed Dynamic Application Data" }, + { 0x9f4c, "ICC Dynamic Number" }, + { 0x9f4d, "Log Entry" }, + { 0x9f4f, "Log Format", EMV_TAG_DOL }, + { 0x9f62, "PCVC3(Track1)" }, + { 0x9f63, "PUNATC(Track1)" }, + { 0x9f64, "NATC(Track1)" }, + { 0x9f65, "PCVC3(Track2)" }, + { 0x9f66, "PUNATC(Track2)" }, + { 0x9f67, "NATC(Track2)" }, + { 0x9f6b, "Track 2 Data" }, + { 0xa5 , "File Control Information (FCI) Proprietary Template" }, + { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, +}; + +static int emv_sort_tag(tlv_tag_t tag) +{ + return (int)(tag >= 0x100 ? tag : tag << 8); +} + +static int emv_tlv_compare(const void *a, const void *b) +{ + const struct tlv *tlv = a; + const struct emv_tag *tag = b; + + return emv_sort_tag(tlv->tag) - (emv_sort_tag(tag->tag)); +} + +static const struct emv_tag *emv_get_tag(const struct tlv *tlv) +{ + struct emv_tag *tag = bsearch(tlv, emv_tags, sizeof(emv_tags)/sizeof(emv_tags[0]), + sizeof(emv_tags[0]), emv_tlv_compare); + + return tag ? tag : &emv_tags[0]; +} + +static const char *bitstrings[] = { + ".......1", + "......1.", + ".....1..", + "....1...", + "...1....", + "..1.....", + ".1......", + "1.......", +}; + +static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + const struct emv_tag_bit *bits = tag->data; + unsigned bit, byte; + + for (byte = 1; byte <= tlv->len; byte ++) { + unsigned char val = tlv->value[byte - 1]; + fprintf(f, "\tByte %u (%02x)\n", byte, val); + for (bit = 8; bit > 0; bit--, val <<= 1) { + if (val & 0x80) + fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], + bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); + if (bits->bit == EMV_BIT(byte, bit)) + bits ++; + } + } +} + +static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + fprintf(f, "\tTag %4hx len %02zx ('%s')\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_fci(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + fprintf(f, "\t--%2hx[%02zx]'%s'\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tString value '"); + fwrite(tlv->value, 1, tlv->len, f); + fprintf(f, "'\n"); +} + +static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, unsigned end) +{ + unsigned long ret = 0; + int i; + + if (end > tlv->len * 2) + return ret; + if (start >= end) + return ret; + + if (start & 1) { + ret += tlv->value[start/2] & 0xf; + i = start + 1; + } else + i = start; + + for (; i < end - 1; i += 2) { + ret *= 10; + ret += tlv->value[i/2] >> 4; + ret *= 10; + ret += tlv->value[i/2] & 0xf; + } + + if (end & 1) { + ret *= 10; + ret += tlv->value[end/2] >> 4; + } + + return ret; +} + +static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tNumeric value %lu\n", emv_value_numeric(tlv, 0, tlv->len * 2)); +} + +static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tDate: 20%02ld.%ld.%ld\n", + emv_value_numeric(tlv, 0, 2), + emv_value_numeric(tlv, 2, 4), + emv_value_numeric(tlv, 4, 6)); +} + +static uint32_t emv_get_binary(const unsigned char *S) +{ + return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0); +} + +static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + uint32_t X, Y; + int i; + + if (tlv->len < 10 || tlv->len % 2) { + fprintf(f, "\tINVALID!\n"); + return; + } + + X = emv_get_binary(tlv->value); + Y = emv_get_binary(tlv->value + 4); + + fprintf(f, "\tX: %d\n", X); + fprintf(f, "\tY: %d\n", Y); + + for (i = 8; i < tlv->len; i+= 2) { + const char *method; + const char *condition; + + switch (tlv->value[i] & 0x3f) { + case 0x0: + method = "Fail CVM processing"; + break; + case 0x1: + method = "Plaintext PIN verification performed by ICC"; + break; + case 0x2: + method = "Enciphered PIN verified online"; + break; + case 0x3: + method = "Plaintext PIN verification performed by ICC and signature (paper)"; + break; + case 0x4: + method = "Enciphered PIN verification performed by ICC"; + break; + case 0x5: + method = "Enciphered PIN verification performed by ICC and signature (paper)"; + break; + case 0x1e: + method = "Signature (paper)"; + break; + case 0x1f: + method = "No CVM required"; + break; + case 0x3f: + method = "NOT AVAILABLE!"; + break; + default: + method = "Unknown"; + break; + } + + switch (tlv->value[i+1]) { + case 0x00: + condition = "Always"; + break; + case 0x01: + condition = "If unattended cash"; + break; + case 0x02: + condition = "If not unattended cash and not manual cash and not purchase with cashback"; + break; + case 0x03: + condition = "If terminal supports the CVM"; + break; + case 0x04: + condition = "If manual cash"; + break; + case 0x05: + condition = "If purchase with cashback"; + break; + case 0x06: + condition = "If transaction is in the application currency and is under X value"; + break; + case 0x07: + condition = "If transaction is in the application currency and is over X value"; + break; + case 0x08: + condition = "If transaction is in the application currency and is under Y value"; + break; + case 0x09: + condition = "If transaction is in the application currency and is over Y value"; + break; + default: + condition = "Unknown"; + break; + } + + fprintf(f, "\t%02x %02x: '%s' '%s' and '%s' if this CVM is unsuccessful\n", + tlv->value[i], tlv->value[i+1], + method, condition, (tlv->value[i] & 0x40) ? "continue" : "fail"); + } +} + +bool emv_tag_dump(const struct tlv *tlv, FILE *f) +{ + if (!tlv) { + fprintf(f, "NULL\n"); + return false; + } + + const struct emv_tag *tag = emv_get_tag(tlv); + + fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); + + switch (tag->type) { + case EMV_TAG_GENERIC: + break; + case EMV_TAG_BITMASK: + emv_tag_dump_bitmask(tlv, tag, f); + break; + case EMV_TAG_DOL: + emv_tag_dump_dol(tlv, tag, f); + break; + case EMV_TAG_CVM_LIST: + emv_tag_dump_cvm_list(tlv, tag, f); + break; + case EMV_TAG_STRING: + emv_tag_dump_string(tlv, tag, f); + break; + case EMV_TAG_NUMERIC: + emv_tag_dump_numeric(tlv, tag, f); + break; + case EMV_TAG_YYMMDD: + emv_tag_dump_yymmdd(tlv, tag, f); + break; + case EMV_TAG_FCI: + emv_tag_dump_fci(tlv, tag, f); + break; + }; + + return true; +} diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h new file mode 100644 index 00000000..de6d9d1e --- /dev/null +++ b/client/emv/emv_tags.h @@ -0,0 +1,24 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef TAGS_H +#define TAGS_H + +#include "tlv.h" +#include + +bool emv_tag_dump(const struct tlv *tlv, FILE *f); + +#endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c new file mode 100644 index 00000000..6f8892fe --- /dev/null +++ b/client/emv/emvcore.c @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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 core functions +//----------------------------------------------------------------------------- + +#include "emvcore.h" + +static bool print_cb(void *data, const struct tlv *tlv) { + emv_tag_dump(tlv, stdout); + dump_buffer(tlv->value, tlv->len, stdout); + + return true; +} + +void TLVPrintFromBuffer(uint8_t *data, int datalen) { + struct tlvdb *t = NULL; + t = tlvdb_parse(data, datalen); + if (t) { + PrintAndLog("TLV decoded:"); + + tlvdb_visit(t, print_cb, NULL); + tlvdb_free(t); + } else { + PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); + } +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h new file mode 100644 index 00000000..523b92ac --- /dev/null +++ b/client/emv/emvcore.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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 core functions +//----------------------------------------------------------------------------- + +#ifndef EMVCORE_H__ +#define EMVCORE_H__ + +#include +#include +#include +#include +#include "util.h" +#include "common.h" +#include "ui.h" +#include "emv/tlv.h" +#include "emv/dump.h" +#include "emv/emv_tags.h" + +extern void TLVPrintFromBuffer(uint8_t *data, int datalen); + +#endif + + + + diff --git a/client/emv/tlv.c b/client/emv/tlv.c new file mode 100644 index 00000000..d78f049e --- /dev/null +++ b/client/emv/tlv.c @@ -0,0 +1,415 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/tlv.c + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" + +#include +#include +#include +#include + +#define TLV_TAG_CLASS_MASK 0xc0 +#define TLV_TAG_COMPLEX 0x20 +#define TLV_TAG_VALUE_MASK 0x1f +#define TLV_TAG_VALUE_CONT 0x1f +#define TLV_TAG_INVALID 0 + +#define TLV_LEN_LONG 0x80 +#define TLV_LEN_MASK 0x7f +#define TLV_LEN_INVALID (~0) + +// http://radek.io/2012/11/10/magical-container_of-macro/ +//#define container_of(ptr, type, member) ({ +// const typeof( ((type *)0)->member ) *__mptr = (ptr); +// (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct tlvdb { + struct tlv tag; + struct tlvdb *next; + struct tlvdb *parent; + struct tlvdb *children; +}; + +struct tlvdb_root { + struct tlvdb db; + size_t len; + unsigned char buf[0]; +}; + +static tlv_tag_t tlv_parse_tag(const unsigned char **buf, size_t *len) +{ + tlv_tag_t tag; + + if (*len == 0) + return TLV_TAG_INVALID; + tag = **buf; + --*len; + ++*buf; + if ((tag & TLV_TAG_VALUE_MASK) != TLV_TAG_VALUE_CONT) + return tag; + + if (*len == 0) + return TLV_TAG_INVALID; + + tag <<= 8; + tag |= **buf; + --*len; + ++*buf; + + return tag; +} + +static size_t tlv_parse_len(const unsigned char **buf, size_t *len) +{ + size_t l; + + if (*len == 0) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + if (!(l & TLV_LEN_LONG)) + return l; + + size_t ll = l &~ TLV_LEN_LONG; + if (*len < ll) + return TLV_LEN_INVALID; + + /* FIXME */ + if (ll != 1) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + return l; +} + +bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv) +{ + tlv->value = 0; + + tlv->tag = tlv_parse_tag(buf, len); + if (tlv->tag == TLV_TAG_INVALID) + return false; + + tlv->len = tlv_parse_len(buf, len); + if (tlv->len == TLV_LEN_INVALID) + return false; + + return true; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent); + +static bool tlvdb_parse_one(struct tlvdb *tlvdb, + struct tlvdb *parent, + const unsigned char **tmp, + size_t *left) +{ + tlvdb->next = tlvdb->children = NULL; + tlvdb->parent = parent; + + tlvdb->tag.tag = tlv_parse_tag(tmp, left); + if (tlvdb->tag.tag == TLV_TAG_INVALID) + goto err; + + tlvdb->tag.len = tlv_parse_len(tmp, left); + if (tlvdb->tag.len == TLV_LEN_INVALID) + goto err; + + if (tlvdb->tag.len > *left) + goto err; + + tlvdb->tag.value = *tmp; + + *tmp += tlvdb->tag.len; + *left -= tlvdb->tag.len; + + if (tlv_is_constructed(&tlvdb->tag) && (tlvdb->tag.len != 0)) { + tlvdb->children = tlvdb_parse_children(tlvdb); + if (!tlvdb->children) + goto err; + } else { + tlvdb->children = NULL; + } + + return true; + +err: + return false; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent) +{ + const unsigned char *tmp = parent->tag.value; + size_t left = parent->tag.len; + struct tlvdb *tlvdb, *first = NULL, *prev = NULL; + + while (left != 0) { + tlvdb = malloc(sizeof(*tlvdb)); + if (prev) + prev->next = tlvdb; + else + first = tlvdb; + prev = tlvdb; + + if (!tlvdb_parse_one(tlvdb, parent, &tmp, &left)) + goto err; + + tlvdb->parent = parent; + } + + return first; + +err: + tlvdb_free(first); + + return NULL; +} + +struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + if (left) + goto err; + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + while (left != 0) { + struct tlvdb *db = malloc(sizeof(*db)); + if (!tlvdb_parse_one(db, NULL, &tmp, &left)) { + free (db); + goto err; + } + + tlvdb_add(&root->db, db); + } + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root) + len); + + root->len = len; + memcpy(root->buf, value, len); + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = root->buf; + + return &root->db; +} + +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root)); + + root->len = 0; + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = value; + + return &root->db; +} + +void tlvdb_free(struct tlvdb *tlvdb) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + tlvdb_free(tlvdb->children); + free(tlvdb); + } +} + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) +{ + while (tlvdb->next) { + tlvdb = tlvdb->next; + } + + tlvdb->next = other; +} + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + cb(data, &tlvdb->tag); + tlvdb_visit(tlvdb->children, cb, data); + } +} + +static const struct tlvdb *tlvdb_next(const struct tlvdb *tlvdb) +{ + if (tlvdb->children) + return tlvdb->children; + + while (tlvdb) { + if (tlvdb->next) + return tlvdb->next; + + tlvdb = tlvdb->parent; + } + + return NULL; +} + +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) +{ + if (prev) { +// tlvdb = tlvdb_next(container_of(prev, struct tlvdb, tag)); + tlvdb = tlvdb_next((struct tlvdb *)prev); + } + + + while (tlvdb) { + if (tlvdb->tag.tag == tag) + return &tlvdb->tag; + + tlvdb = tlvdb_next(tlvdb); + } + + return NULL; +} + +unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) +{ + size_t size = tlv->len; + unsigned char *data; + size_t pos; + + if (tlv->tag > 0x100) + size += 2; + else + size += 1; + + if (tlv->len > 0x7f) + size += 2; + else + size += 1; + + data = malloc(size); + if (!data) { + *len = 0; + return NULL; + } + + pos = 0; + + if (tlv->tag > 0x100) { + data[pos++] = tlv->tag >> 8; + data[pos++] = tlv->tag & 0xff; + } else + data[pos++] = tlv->tag; + + if (tlv->len > 0x7f) { + data[pos++] = 0x81; + data[pos++] = tlv->len; + } else + data[pos++] = tlv->len; + + memcpy(data + pos, tlv->value, tlv->len); + pos += tlv->len; + + *len = pos; + return data; +} + +bool tlv_is_constructed(const struct tlv *tlv) +{ + return (tlv->tag < 0x100 ? tlv->tag : tlv->tag >> 8) & TLV_TAG_COMPLEX; +} + +bool tlv_equal(const struct tlv *a, const struct tlv *b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); +} diff --git a/client/emv/tlv.h b/client/emv/tlv.h new file mode 100644 index 00000000..3fd3f347 --- /dev/null +++ b/client/emv/tlv.h @@ -0,0 +1,52 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/include/openemv/tlv.h + */ + +#ifndef TLV_H +#define TLV_H + +#include +#include +#include + +typedef uint16_t tlv_tag_t; + +struct tlv { + tlv_tag_t tag; + size_t len; + const unsigned char *value; +}; + +struct tlvdb; +typedef bool (*tlv_cb)(void *data, const struct tlv *tlv); + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value); +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value); +struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); +void tlvdb_free(struct tlvdb *tlvdb); + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data); +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); + +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); +bool tlv_is_constructed(const struct tlv *tlv); +bool tlv_equal(const struct tlv *a, const struct tlv *b); + +#endif From c5e9a0212e68f864371439cb5be8fd227002e9c8 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 16:55:12 +0200 Subject: [PATCH 14/23] deleted condition in iso14a select (sak & 0x20 ==0) - error. Usually SAK = 88 --- armsrc/iso14443a.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 2ff722b0..01026aa5 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1817,7 +1817,8 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u } // non iso14443a compliant tag - if( (sak & 0x20) == 0) return 2; + // now not work + //if( (sak & 0x20) == 0) return 2; if (!no_rats) { // Request for answer to select From 89ec86abd293e6294223e971278caf4aad2be190 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 17:02:35 +0200 Subject: [PATCH 15/23] now SAK check is OK --- armsrc/iso14443a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 01026aa5..860af238 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1817,8 +1817,8 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u } // non iso14443a compliant tag - // now not work - //if( (sak & 0x20) == 0) return 2; + // https://www.nxp.com/docs/en/application-note/AN10834.pdf page 7 + if( (sak & 0x20) != 0) return 2; if (!no_rats) { // Request for answer to select From 7376da5c44ac5d33c3515730f7213a64d51d696a Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 18:22:03 +0200 Subject: [PATCH 16/23] revert SAK check --- armsrc/iso14443a.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 7d484015..33db9f22 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1820,9 +1820,8 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u p_hi14a_card->sak = sak; } - // non iso14443a compliant tag - // https://www.nxp.com/docs/en/application-note/AN10834.pdf page 7 - if( (sak & 0x20) != 0) return 2; + // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) + if( (sak & 0x20) == 0) return 2; if (!no_rats) { // Request for answer to select From 499df9088d5845d954918da51dc339becd4a7da4 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 18:34:15 +0200 Subject: [PATCH 17/23] fix behavier reading iso14443-3 and iso14443-4 cards --- armsrc/iso14443a.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 33db9f22..c39b8a20 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1935,7 +1935,10 @@ void ReaderIso14443a(UsbCommand *c) arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); // if we cant select then we cant send data - cantSELECT = (arg0 != 1); + if (arg0 != 1 && arg0 != 2) { + // 1 - all is OK with ATS, 2 - without ATS + cantSELECT = true; + } LED_B_ON(); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); From 23207d748f65f97365a608f2a9abfc3ac5997413 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 2 Nov 2017 14:11:42 +0200 Subject: [PATCH 18/23] tlv decoder works. --- client/cmdhf14a.c | 2 +- client/emv/emv_tags.c | 25 +------------------------ client/emv/emvcore.c | 2 +- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index f3189c10..86cfea2f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -760,7 +760,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); - // here TLV decoder... + // TLV decoder if (decodeTLV && datalen > 4) { TLVPrintFromBuffer(data, datalen - 2); } diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 9bcf2004..1aae847a 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -30,7 +30,6 @@ enum emv_tag_t { EMV_TAG_STRING, EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, - EMV_TAG_FCI, }; struct emv_tag { @@ -128,7 +127,7 @@ static const struct emv_tag emv_tags[] = { { 0x5f30, "Service Code", EMV_TAG_NUMERIC }, { 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC }, { 0x61 , "Application Template" }, - { 0x6f , "File Control Information (FCI) Template", EMV_TAG_FCI }, + { 0x6f , "File Control Information (FCI) Template" }, { 0x70 , "READ RECORD Response Message Template" }, { 0x77 , "Response Message Template Format 2" }, { 0x80 , "Response Message Template Format 1" }, @@ -269,25 +268,6 @@ static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, F } } -static void emv_tag_dump_fci(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { - const unsigned char *buf = tlv->value; - size_t left = tlv->len; - - while (left) { - struct tlv doltlv; - const struct emv_tag *doltag; - - if (!tlv_parse_tl(&buf, &left, &doltlv)) { - fprintf(f, "Invalid Tag-Len\n"); - continue; - } - - doltag = emv_get_tag(&doltlv); - - fprintf(f, "\t--%2hx[%02zx]'%s'\n", doltlv.tag, doltlv.len, doltag->name); - } -} - static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { fprintf(f, "\tString value '"); @@ -471,9 +451,6 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f) case EMV_TAG_YYMMDD: emv_tag_dump_yymmdd(tlv, tag, f); break; - case EMV_TAG_FCI: - emv_tag_dump_fci(tlv, tag, f); - break; }; return true; diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 6f8892fe..9264b110 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -19,7 +19,7 @@ static bool print_cb(void *data, const struct tlv *tlv) { void TLVPrintFromBuffer(uint8_t *data, int datalen) { struct tlvdb *t = NULL; - t = tlvdb_parse(data, datalen); + t = tlvdb_parse_multi(data, datalen); if (t) { PrintAndLog("TLV decoded:"); From 1d6f7bc8ac10ed1761f9181eb74245e53ef6d978 Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 2 Nov 2017 14:28:05 +0200 Subject: [PATCH 19/23] added changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60caf413..10e6b827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - `hf mf chk` Move main cycle to arm (Merlok) - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) - Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) +- Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -35,6 +36,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to proxmark ability to execute commands from stdin (pipe) (Merlok) - Added `hf 14a info` and moved there functionality from `hf 14a reader` (Merlok) - Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) +- Added to `hf 14a apdu` - exchange apdu via iso1443-4 (Merlok) +- Added to `hf 14a apdu` - apdu and tlv results parser (Merlok) ## [3.0.1][2017-06-08] From eb6e8de45dcbf97b15812f88cfeb6b525a5e566e Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 3 Nov 2017 13:42:38 +0200 Subject: [PATCH 20/23] moved includes, added clear_trace flag (it was there...) and fixed multiapdu iso14443 stream --- armsrc/iso14443a.c | 7 +++---- client/cmdhf14a.c | 37 +++++++++++++++++++++++++++++++++---- client/cmdhf14a.h | 15 +-------------- include/mifare.h | 3 ++- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 5a27e06b..90e8538e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1916,19 +1916,18 @@ void ReaderIso14443a(UsbCommand *c) uint8_t par[MAX_PARITY_SIZE]; bool cantSELECT = false; - if(param & ISO14A_CONNECT) { + set_tracing(true); + + if(param & ISO14A_CLEAR_TRACE) { clear_trace(); } - set_tracing(true); - if(param & ISO14A_REQUEST_TRIGGER) { iso14a_set_trigger(true); } if(param & ISO14A_CONNECT) { LED_A_ON(); - clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 1ae63ada..c2d12fd9 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -11,9 +11,25 @@ #include "cmdhf14a.h" +#include "util.h" +#include "util_posix.h" +#include "iso14443crc.h" +#include "data.h" +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "cmdmain.h" +#include "mifare.h" +#include "cmdhfmfu.h" +#include "mifarehost.h" +#include "emv/apduinfo.h" +#include "emv/emvcore.h" + static int CmdHelp(const char *Cmd); static int waitCmd(uint8_t iLen); + const manufactureName manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, @@ -621,17 +637,22 @@ int CmdHF14ASnoop(const char *Cmd) { int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { uint8_t data[USB_CMD_DATA_SIZE]; int datalen; - uint8_t cmdc = 0; + uint16_t cmdc = 0; uint8_t first, second; + static uint8_t iso14_pcb_blocknum; - if (activateField) - cmdc |= ISO14A_CONNECT; + if (activateField) { + cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; + iso14_pcb_blocknum = 0; + } if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 memcpy(data + 1, datain, datainlen); data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + data[0] += iso14_pcb_blocknum; // add block number (bnr) + datalen = datainlen + 1; ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); @@ -673,6 +694,14 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea PrintAndLog("APDU ERROR: Small APDU response."); return 2; } + + // invert block number field in PCB byte + if ( ((recv[0] & 0xC0) == 0 // I-Block + || (recv[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 + && (recv[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers + { + iso14_pcb_blocknum ^= 1; + } // check block if (data[0] != recv[0]) { @@ -905,7 +934,7 @@ int CmdHF14ACmdRaw(const char *cmd) { if(active || active_select) { - c.arg[0] |= ISO14A_CONNECT; + c.arg[0] |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; if(active) c.arg[0] |= ISO14A_NO_SELECT; } diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 16e03574..e7784de2 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -18,20 +18,7 @@ #include #include #include -#include "util.h" -#include "util_posix.h" -#include "iso14443crc.h" -#include "data.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" -#include "common.h" -#include "cmdmain.h" -#include "mifare.h" -#include "cmdhfmfu.h" -#include "mifarehost.h" -#include "emv/apduinfo.h" -#include "emv/emvcore.h" +#include // structure and database for uid -> tagtype lookups typedef struct { diff --git a/include/mifare.h b/include/mifare.h index bede67a9..b821f32b 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -35,7 +35,8 @@ typedef enum ISO14A_COMMAND { ISO14A_SET_TIMEOUT = (1 << 6), ISO14A_NO_SELECT = (1 << 7), ISO14A_TOPAZMODE = (1 << 8), - ISO14A_NO_RATS = (1 << 9) + ISO14A_NO_RATS = (1 << 9), + ISO14A_CLEAR_TRACE = (1 << 10) } iso14a_command_t; typedef struct { From b7d3e89923d418c8a7b6acf1ca74f45b436be26d Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 9 Nov 2017 16:39:40 +0200 Subject: [PATCH 21/23] changed iso14aapdu arm side and client --- armsrc/iso14443a.c | 45 ++++++++++++++++------- client/cmdhf14a.c | 91 +++++++++++++++++----------------------------- client/cmdhf14a.h | 1 + 3 files changed, 66 insertions(+), 71 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 90e8538e..45425e07 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -12,10 +12,11 @@ #include "iso14443a.h" +#include +#include #include "proxmark3.h" #include "apps.h" #include "util.h" -#include "string.h" #include "cmd.h" #include "iso14443crc.h" #include "crapto1/crapto1.h" @@ -1873,29 +1874,47 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { uint8_t parity[MAX_PARITY_SIZE]; - uint8_t real_cmd[cmd_len+4]; - real_cmd[0] = 0x0a; //I-Block + uint8_t real_cmd[cmd_len + 4]; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) // put block number into the PCB real_cmd[0] |= iso14_pcb_blocknum; - real_cmd[1] = 0x00; //CID: 0 //FIXME: allow multiple selected cards - memcpy(real_cmd+2, cmd, cmd_len); - AppendCrc14443a(real_cmd,cmd_len+2); + memcpy(real_cmd + 1, cmd, cmd_len); + AppendCrc14443a(real_cmd, cmd_len + 1); - ReaderTransmit(real_cmd, cmd_len+4, NULL); + ReaderTransmit(real_cmd, cmd_len + 3, NULL); + size_t len = ReaderReceive(data, parity); uint8_t *data_bytes = (uint8_t *) data; - if (!len) + + if (!len) { return 0; //DATA LINK ERROR + // if we received an I- or R(ACK)-Block with a block number equal to the // current block number, toggle the current block number - else if (len >= 4 // PCB+CID+CRC = 4 bytes + } else{ + if (len >= 3 // PCB+CRC = 3 bytes && ((data_bytes[0] & 0xC0) == 0 // I-Block || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers - { - iso14_pcb_blocknum ^= 1; - } + { + iso14_pcb_blocknum ^= 1; + } + // crc check + if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) { + return -1; + } + + } + + // cut frame byte + len -= 1; + // memmove(data_bytes, data_bytes + 1, len); + for (int i = 0; i < len; i++) + data_bytes[i] = data_bytes[i + 1]; + return len; } @@ -1952,7 +1971,7 @@ void ReaderIso14443a(UsbCommand *c) if(param & ISO14A_APDU && !cantSELECT) { arg0 = iso14_apdu(cmd, len, buf); LED_B_ON(); - cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); LED_B_OFF(); } diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index c2d12fd9..18e13794 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -634,87 +634,73 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +void DropField() { + UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; + SendCommand(&c); +} + int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { - uint8_t data[USB_CMD_DATA_SIZE]; - int datalen; uint16_t cmdc = 0; - uint8_t first, second; - static uint8_t iso14_pcb_blocknum; if (activateField) { cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; - iso14_pcb_blocknum = 0; } if (leaveSignalON) cmdc |= ISO14A_NO_DISCONNECT; - // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 - memcpy(data + 1, datain, datainlen); - data[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) - data[0] += iso14_pcb_blocknum; // add block number (bnr) - - datalen = datainlen + 1; - - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; - // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length USB_CMD_DATA_SIZE=512 // timeout timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_SET_TIMEOUT | cmdc, (datalen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; - memcpy(c.d.asBytes, data, datalen); + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_SET_TIMEOUT | cmdc, (datainlen & 0xFFFF), 1000 * 1000 * 1.06 / 100}}; + memcpy(c.d.asBytes, datain, datainlen); SendCommand(&c); uint8_t *recv; UsbCommand resp; if (activateField) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("APDU ERROR: Proxmark connection timeout."); return 1; - if (resp.arg[0] != 1) + } + if (resp.arg[0] != 1) { + PrintAndLog("APDU ERROR: Proxmark error %d.", resp.arg[0]); return 1; + } } if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { recv = resp.d.asBytes; - uint8_t iLen = resp.arg[0]; + int iLen = resp.arg[0]; - *dataoutlen = iLen - 1 - 2; + *dataoutlen = iLen - 2; if (*dataoutlen < 0) *dataoutlen = 0; - memcpy(dataout, recv + 1, *dataoutlen); + memcpy(dataout, recv, *dataoutlen); - if(!iLen) + if(!iLen) { + PrintAndLog("APDU ERROR: No APDU response."); return 1; - - // check apdu length - if (iLen < 5) { - PrintAndLog("APDU ERROR: Small APDU response."); - return 2; } - // invert block number field in PCB byte - if ( ((recv[0] & 0xC0) == 0 // I-Block - || (recv[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 - && (recv[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers - { - iso14_pcb_blocknum ^= 1; - } - - // check block - if (data[0] != recv[0]) { - PrintAndLog("APDU ERROR: Block type mismatch: %02x-%02x", data[0], recv[0]); + // check block TODO + if (iLen == -2) { + PrintAndLog("APDU ERROR: Block type mismatch."); return 2; } // CRC Check - ComputeCrc14443(CRC_14443_A, recv, iLen, &first, &second); - if (first || second) { + if (iLen == -1) { PrintAndLog("APDU ERROR: ISO 14443A CRC error."); return 3; } + + // check apdu length + if (iLen < 4) { + PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen); + return 2; + } } else { PrintAndLog("APDU ERROR: Reply timeout."); @@ -782,23 +768,12 @@ int CmdHF14AAPDU(const char *cmd) { cmdp++; } - PrintAndLog("--%s %s %s >>>> %s", activateField ? "sel": "", leaveSignalON ? "keep": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); + PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - switch(ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen)) { - case 0: - break; - case 1: - PrintAndLog("APDU ERROR: Send APDU error."); - return 1; - case 2: - return 2; - case 3: - return 3; - case 4: - return 4; - default: - return 5; - } + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen); + + if (res) + return res; PrintAndLog("<<<< %s", sprint_hex(data, datalen)); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index e7784de2..5276da36 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -35,6 +35,7 @@ int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); +extern void DropField(); extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen); #endif From 86a1c1dc252076f6c5d678f172df9ff116bea50d Mon Sep 17 00:00:00 2001 From: merlokk Date: Thu, 9 Nov 2017 22:01:00 +0200 Subject: [PATCH 22/23] eliminate double DropField command --- client/cmdhfmfu.c | 6 ------ client/cmdhfmfu.h | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 3021631a..c95fa20b 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -113,12 +113,6 @@ static void ul_switch_on_field(void) { SendCommand(&c); } -void ul_switch_off_field(void) { - UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); -} - static int ul_send_cmd_raw( uint8_t *cmd, uint8_t cmdlen, uint8_t *response, uint16_t responseLength ) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_APPEND_CRC, cmdlen, 0}}; memcpy(c.d.asBytes, cmd, cmdlen); diff --git a/client/cmdhfmfu.h b/client/cmdhfmfu.h index 6c9e3ea1..a5e9f95c 100644 --- a/client/cmdhfmfu.h +++ b/client/cmdhfmfu.h @@ -16,7 +16,7 @@ int CmdHF14AMfUInfo(const char *Cmd); uint32_t GetHF14AMfU_Type(void); int ul_print_type(uint32_t tagtype, uint8_t spacer); -void ul_switch_off_field(void); +#define ul_switch_off_field DropField int usage_hf_mfu_dump(void); int usage_hf_mfu_info(void); From 9658b9e137dbeb2f81ec3d72d3045c1ee22650dd Mon Sep 17 00:00:00 2001 From: merlokk Date: Fri, 10 Nov 2017 09:15:56 +0200 Subject: [PATCH 23/23] move include modules from h to c file --- client/cmdhf14a.c | 5 +++++ client/cmdhf14a.h | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 18e13794..24a66b88 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -11,6 +11,11 @@ #include "cmdhf14a.h" +#include +#include +#include +#include +#include #include "util.h" #include "util_posix.h" #include "iso14443crc.h" diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 5276da36..6f6df907 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -12,12 +12,7 @@ #ifndef CMDHF14A_H__ #define CMDHF14A_H__ -#include #include -#include -#include -#include -#include #include // structure and database for uid -> tagtype lookups