From 6b6c3be6b9a452af629c6b4f06d44f3ab2fd40c6 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 12 Jan 2019 13:24:22 +0100 Subject: [PATCH] Added ATR decoding (RfidResearchGroup PRs 67/68 by @merlokk) (#749) ... and fixed merge errors in cmdsmartcard.c --- client/cmdsmartcard.c | 299 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 276 insertions(+), 23 deletions(-) diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c index 4258989c..c6f08aa4 100644 --- a/client/cmdsmartcard.c +++ b/client/cmdsmartcard.c @@ -20,6 +20,7 @@ #include "cmdhf.h" // CmdHFlist #include "emv/apduinfo.h" // APDUcode description #include "emv/emvcore.h" // decodeTVL +#include "emv/dump.h" // dump_buffer static int CmdHelp(const char *Cmd); @@ -90,6 +91,193 @@ static int usage_sm_brute(void) { return 0; } +uint8_t GetATRTA1(uint8_t *atr, size_t atrlen) { + if (atrlen > 2) { + uint8_t T0 = atr[1]; + if (T0 & 0x10) + return atr[2]; + } + + return 0x11; // default value is 0x11, corresponding to fmax=5 MHz, Fi=372, Di=1. +} + +int DiArray[] = { + 0, // b0000 RFU + 1, // b0001 + 2, + 4, + 8, + 16, + 32, // b0110 + 64, // b0111. This was RFU in ISO/IEC 7816-3:1997 and former. Some card readers or drivers may erroneously reject cards using this value + 12, + 20, + 0, // b1010 RFU + 0, + 0, // ... + 0, + 0, + 0 // b1111 RFU +}; + +int FiArray[] = { + 372, // b0000 Historical note: in ISO/IEC 7816-3:1989, this was assigned to cards with internal clock + 372, // b0001 + 558, // b0010 + 744, // b0011 + 1116, // b0100 + 1488, // b0101 + 1860, // b0110 + 0, // b0111 RFU + 0, // b1000 RFU + 512, // b1001 + 768, // b1010 + 1024, // b1011 + 1536, // b1100 + 2048, // b1101 + 0, // b1110 RFU + 0 // b1111 RFU +}; + +float FArray[] = { + 4, // b0000 Historical note: in ISO/IEC 7816-3:1989, this was assigned to cards with internal clock + 5, // b0001 + 6, // b0010 + 8, // b0011 + 12, // b0100 + 16, // b0101 + 20, // b0110 + 0, // b0111 RFU + 0, // b1000 RFU + 5, // b1001 + 7.5, // b1010 + 10, // b1011 + 15, // b1100 + 20, // b1101 + 0, // b1110 RFU + 0 // b1111 RFU +}; + +int GetATRDi(uint8_t *atr, size_t atrlen) { + uint8_t TA1 = GetATRTA1(atr, atrlen); + + return DiArray[TA1 & 0x0f]; // The 4 low-order bits of TA1 (4th MSbit to 1st LSbit) encode Di +} + +int GetATRFi(uint8_t *atr, size_t atrlen) { + uint8_t TA1 = GetATRTA1(atr, atrlen); + + return FiArray[TA1 >> 4]; // The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi +} + +float GetATRF(uint8_t *atr, size_t atrlen) { + uint8_t TA1 = GetATRTA1(atr, atrlen); + + return FArray[TA1 >> 4]; // The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi +} + +static int PrintATR(uint8_t *atr, size_t atrlen) { + uint8_t vxor = 0; + for (int i = 1; i < atrlen; i++) + vxor ^= atr[i]; + + if (vxor) + PrintAndLogEx(WARNING, "Check summ error. Must be 0 but: 0x%02x", vxor); + else + PrintAndLogEx(INFO, "Check summ OK."); + + if (atr[0] != 0x3b) + PrintAndLogEx(WARNING, "Not a direct convention: 0x%02x", atr[0]); + + uint8_t T0 = atr[1]; + uint8_t K = T0 & 0x0F; + uint8_t TD1 = 0; + + uint8_t T1len = 0; + uint8_t TD1len = 0; + uint8_t TDilen = 0; + + if (T0 & 0x10) { + PrintAndLog("TA1 (Maximum clock frequency, proposed bit duration): 0x%02x", atr[2 + T1len]); + T1len++; + } + if (T0 & 0x20) { + PrintAndLog("TB1 (Deprecated: VPP requirements): 0x%02x", atr[2 + T1len]); + T1len++; + } + if (T0 & 0x40) { + PrintAndLog("TC1 (Extra delay between bytes required by card): 0x%02x", atr[2 + T1len]); + T1len++; + } + if (T0 & 0x80) { + TD1 = atr[2 + T1len]; + PrintAndLog("TD1 (First offered transmission protocol, presence of TA2..TD2): 0x%02x. Protocol T=%d", TD1, TD1 & 0x0f); + T1len++; + + if (TD1 & 0x10) { + PrintAndLog("TA2 (Specific protocol and parameters to be used after the ATR): 0x%02x", atr[2 + T1len + TD1len]); + TD1len++; + } + if (TD1 & 0x20) { + PrintAndLog("TB2 (Deprecated: VPP precise voltage requirement): 0x%02x", atr[2 + T1len + TD1len]); + TD1len++; + } + if (TD1 & 0x40) { + PrintAndLog("TC2 (Maximum waiting time for protocol T=0): 0x%02x", atr[2 + T1len + TD1len]); + TD1len++; + } + if (TD1 & 0x80) { + uint8_t TDi = atr[2 + T1len + TD1len]; + PrintAndLog("TD2 (A supported protocol or more global parameters, presence of TA3..TD3): 0x%02x. Protocol T=%d", TDi, TDi & 0x0f); + TD1len++; + + bool nextCycle = true; + uint8_t vi = 3; + while (nextCycle) { + nextCycle = false; + if (TDi & 0x10) { + PrintAndLog("TA%d: 0x%02x", vi, atr[2 + T1len + TD1len + TDilen]); + TDilen++; + } + if (TDi & 0x20) { + PrintAndLog("TB%d: 0x%02x", vi, atr[2 + T1len + TD1len + TDilen]); + TDilen++; + } + if (TDi & 0x40) { + PrintAndLog("TC%d: 0x%02x", vi, atr[2 + T1len + TD1len + TDilen]); + TDilen++; + } + if (TDi & 0x80) { + TDi = atr[2 + T1len + TD1len + TDilen]; + PrintAndLog("TD%d: 0x%02x. Protocol T=%d", vi, TDi, TDi & 0x0f); + TDilen++; + + nextCycle = true; + vi++; + } + } + } + } + + uint8_t calen = 2 + T1len + TD1len + TDilen + K; + + if (atrlen != calen && atrlen != calen + 1) // may be CRC + PrintAndLogEx(ERR, "ATR length error. len: %d, T1len: %d, TD1len: %d, TDilen: %d, K: %d", atrlen, T1len, TD1len, TDilen, K); + else + PrintAndLogEx(INFO, "ATR length OK."); + + PrintAndLog("Historical bytes len: 0x%02x", K); + if (K > 0) + PrintAndLog("The format of historical bytes: %02x", atr[2 + T1len + TD1len + TDilen]); + if (K > 1) { + PrintAndLog("Historical bytes:"); + dump_buffer(&atr[2 + T1len + TD1len + TDilen], K, NULL, 1); + } + + return 0; +} + + static bool smart_select(bool silent) { UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; clearCommandBuffer(); @@ -137,29 +325,55 @@ static int smart_wait(uint8_t *data) { return len; } -static int smart_response(uint8_t *data) { +static int smart_response(uint8_t apduINS, uint8_t *data) { - int len = -1; int datalen = smart_wait(data); + bool needGetData = false; - if ( data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F ) { - len = data[datalen - 1]; + if (datalen < 2 ) { + goto out; } - if (len == -1 ) { + if (datalen > 2 && data[0] != apduINS) { + PrintAndLogEx(ERR, "Card ACK error. len=0x%x data[0]=%02x", datalen, data[0]); + datalen = 0; goto out; } - PrintAndLogEx(INFO, "Requesting response. len=0x%x", len); - uint8_t getstatus[] = {ISO7816_GETSTATUS, 0x00, 0x00, len}; - UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}}; - memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) ); - clearCommandBuffer(); - SendCommand(&cStatus); + if ( data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F ) { + needGetData = true; + } - datalen = smart_wait(data); -out: + if (needGetData) { + int len = data[datalen - 1]; + PrintAndLogEx(INFO, "Requesting response. len=0x%x", len); + uint8_t getstatus[] = {ISO7816_GETSTATUS, 0x00, 0x00, len}; + UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}}; + memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) ); + clearCommandBuffer(); + SendCommand(&cStatus); + + datalen = smart_wait(data); + + if (datalen < 2 ) { + goto out; + } + if (datalen > 2 && data[0] != ISO7816_GETSTATUS) { + PrintAndLogEx(ERR, "GetResponse ACK error. len=0x%x data[0]=%02x", len, data[0]); + datalen = 0; + goto out; + } + if (datalen != len + 2 + 1) { // 2 - response, 1 - ACK + PrintAndLogEx(WARNING, "GetResponse wrong length. Must be: 0x%02x but: 0x%02x", len, datalen - 3); + } + } + + if (datalen > 2) { + datalen--; + memmove(data, &data[1], datalen); + } + out: return datalen; } @@ -225,13 +439,13 @@ int CmdSmartRaw(const char *Cmd) { UsbCommand c = {CMD_SMART_RAW, {0, hexlen, 0}}; if (active || active_select) { - c.arg[0] |= SC_CONNECT; - if (active_select) - c.arg[0] |= SC_SELECT; - } + c.arg[0] |= SC_CONNECT; + if (active_select) + c.arg[0] |= SC_SELECT; + } if (hexlen > 0) { - c.arg[0] |= SC_RAW; + c.arg[0] |= SC_RAW; } memcpy(c.d.asBytes, data, hexlen ); @@ -245,7 +459,7 @@ int CmdSmartRaw(const char *Cmd) { if ( !buf ) return 1; - int len = smart_response(buf); + int len = smart_response(data[1], buf); if ( len < 0 ) { free(buf); return 2; @@ -257,7 +471,7 @@ int CmdSmartRaw(const char *Cmd) { memcpy(c.d.asBytes, data, sizeof(data) ); clearCommandBuffer(); SendCommand(&c); - len = smart_response(buf); + len = smart_response(data[1], buf); data[4] = 0; } @@ -285,12 +499,29 @@ int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leave clearCommandBuffer(); SendCommand(&c); - int len = smart_response(dataout); + int len = smart_response(datain[1], dataout); if ( len < 0 ) { return 2; } + + // retry + if (len > 1 && dataout[len - 2] == 0x6c && datainlen > 4) { + UsbCommand c2 = {CMD_SMART_RAW, {SC_RAW, datainlen, 0}}; + memcpy(c2.d.asBytes, datain, datainlen); + + int vlen = 5 + datain[4]; + if (datainlen == vlen) + datainlen++; + + c2.d.asBytes[vlen] = dataout[len - 1]; + + clearCommandBuffer(); + SendCommand(&c2); + + len = smart_response(datain[1], dataout); + } *dataoutlen = len; return 0; @@ -449,6 +680,28 @@ int CmdSmartInfo(const char *Cmd){ PrintAndLogEx(INFO, "ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len)); PrintAndLogEx(INFO, "look up ATR"); PrintAndLogEx(INFO, "http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) ); + + // print ATR + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "* ATR:"); + PrintATR(card.atr, card.atr_len); + + // print D/F (brom byte TA1 or defaults) + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "* D/F (TA1):"); + int Di = GetATRDi(card.atr, card.atr_len); + int Fi = GetATRFi(card.atr, card.atr_len); + float F = GetATRF(card.atr, card.atr_len); + if (GetATRTA1(card.atr, card.atr_len) == 0x11) + PrintAndLogEx(INFO, "Using default values..."); + + PrintAndLogEx(NORMAL, "Di=%d", Di); + PrintAndLogEx(NORMAL, "Fi=%d", Fi); + PrintAndLogEx(NORMAL, "F=%.1f MHz", F); + PrintAndLogEx(NORMAL, "Cycles/ETU=%d", Fi/Di); + PrintAndLogEx(NORMAL, "%.1f bits/sec at 4MHz", (float)4000000 / (Fi/Di)); + PrintAndLogEx(NORMAL, "%.1f bits/sec at Fmax=%.1fMHz", (F * 1000000) / (Fi/Di), F); + return 0; } @@ -587,7 +840,7 @@ int CmdSmartBruteforceSFI(const char *Cmd) { clearCommandBuffer(); SendCommand(&c); - smart_response(buf); + smart_response(data[1], buf); // if 0x6C if ( buf[0] == 0x6C ) { @@ -596,7 +849,7 @@ int CmdSmartBruteforceSFI(const char *Cmd) { memcpy(c.d.asBytes, data, sizeof(data) ); clearCommandBuffer(); SendCommand(&c); - uint8_t len = smart_response(buf); + uint8_t len = smart_response(data[1], buf); // TLV decoder if (len > 4) -- 2.39.5