From: pwpiwi Date: Sat, 5 Jan 2019 08:47:12 +0000 (+0100) Subject: Emv commands work with smartcard interface (RfidResearchGroup PR67 by @Merlokk) ... X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/8d7d7b618777fddcde8897945f2ec42eb85095e2?ds=sidebyside Emv commands work with smartcard interface (RfidResearchGroup PR67 by @Merlokk) (#743) * replace 'hf emv' commands by 'emv' commands * Enable smartcard commands by default (-DWITH_SMARTCARD) * update i2c.c from RfidResearchGroup repository * update smartcard.c from RfidResearchGroup repository --- diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ed8878..8b856ec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `lf hitag reader 03` - read block (instead of pages) - Added `lf hitag reader 04` - read block (instead of pages) - Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (Merlok) +- Added `emv` commmands working for both contactless and smart cards (Merlok) ## [v3.1.0][2018-10-10] diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 7efb1fd3..bd22e19a 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -24,7 +24,6 @@ #endif -// ¶¨ÒåÁ¬½ÓÒý½Å #define GPIO_RST AT91C_PIO_PA1 #define GPIO_SCL AT91C_PIO_PA5 #define GPIO_SDA AT91C_PIO_PA7 @@ -49,14 +48,41 @@ static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { for (c = delay * 2; c; c--) {}; } -// ͨѶÑÓ³Ùº¯Êý communication delay function +// communication delay functions #define I2C_DELAY_1CLK I2CSpinDelayClk(1) #define I2C_DELAY_2CLK I2CSpinDelayClk(2) #define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) - #define ISO7618_MAX_FRAME 255 +// try i2c bus recovery at 100kHz = 5uS high, 5uS low +static void I2C_recovery(void) { + + DbpString("Performing i2c bus recovery"); + + // reset I2C + SDA_H; SCL_H; + + //9nth cycle acts as NACK + for (int i = 0; i < 10; i++) { + SCL_H; WaitUS(5); + SCL_L; WaitUS(5); + } + + //a STOP signal (SDA from low to high while CLK is high) + SDA_L; WaitUS(5); + SCL_H; WaitUS(2); + SDA_H; WaitUS(2); + + bool isok = (SCL_read && SDA_read); + if (!SDA_read) + DbpString("I2C bus recovery error: SDA still LOW"); + if (!SCL_read) + DbpString("I2C bus recovery error: SCL still LOW"); + if (isok) + DbpString("I2C bus recovery complete"); +} + static void I2C_init(void) { // Configure reset pin AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; // disable pull up resistor @@ -72,10 +98,13 @@ static void I2C_init(void) { // configure all three pins as output, controlled by PIOA AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); -} + bool isok = (SCL_read && SDA_read); + if ( !isok ) + I2C_recovery(); + +} -// ÉèÖø´Î»×´Ì¬ // set the reset state static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { if (LineRST) @@ -94,19 +123,19 @@ static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA LOW(GPIO_SDA); } -// ¸´Î»½øÈëÖ÷³ÌÐò // Reset the SIM_Adapter, then enter the main program // Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter. static void I2C_Reset_EnterMainProgram(void) { - I2C_SetResetStatus(0, 0, 0); // À­µÍ¸´Î»Ïß - SpinDelay(30); - I2C_SetResetStatus(1, 0, 0); // ½â³ý¸´Î» - SpinDelay(30); - I2C_SetResetStatus(1, 1, 1); // À­¸ßÊý¾ÝÏß - SpinDelay(10); + StartTicks(); + I2C_init(); + I2C_SetResetStatus(0, 0, 0); + WaitMS(30); + I2C_SetResetStatus(1, 0, 0); + WaitMS(30); + I2C_SetResetStatus(1, 1, 1); + WaitMS(10); } -// µÈ´ýʱÖÓ±ä¸ß // Wait for the clock to go High. static bool WaitSCL_H_delay(uint32_t delay) { while (delay--) { @@ -184,7 +213,8 @@ static void I2C_SendByte(uint8_t data) { uint8_t bits = 8; while (bits--) { - SCL_L; I2C_DELAY_1CLK; + SCL_L; + I2C_DELAY_1CLK; if (data & 0x80) SDA_H; @@ -205,7 +235,6 @@ static void I2C_SendByte(uint8_t data) { } bool I2C_is_available(void) { - I2C_init(); I2C_Reset_EnterMainProgram(); if (!I2C_Start()) // some other device is active on the bus return true; @@ -219,14 +248,13 @@ bool I2C_is_available(void) { } #ifdef WITH_SMARTCARD -// ¸´Î»½øÈëÒýµ¼Ä£Ê½ // Reset the SIM_Adapter, then enter the bootloader program // Reserve£ºFor firmware update. static void I2C_Reset_EnterBootloader(void) { - I2C_SetResetStatus(0, 1, 1); // À­µÍ¸´Î»Ïß - SpinDelay(100); - I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î» - SpinDelay(10); + I2C_SetResetStatus(0, 1, 1); + WaitMS(100); + I2C_SetResetStatus(1, 1, 1); + WaitMS(10); } // Wait max 300ms or until SCL goes LOW. @@ -238,7 +266,7 @@ static bool WaitSCL_L_300ms(void) { if (!SCL_read) return true; - SpinDelay(1); + WaitMS(1); } return (delay == 0); } @@ -303,7 +331,7 @@ static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { do { if (!I2C_Start()) return false; - //[C0] + I2C_SendByte(device_address & 0xFE); if (!I2C_WaitAck()) break; @@ -323,7 +351,6 @@ static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { return true; } -// дÈë1×Ö½ÚÊý¾Ý £¨´ýдÈëÊý¾Ý£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© // Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { bool bBreak = true; @@ -354,7 +381,6 @@ static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_addre return true; } -// дÈë1´®Êý¾Ý£¨´ýдÈëÊý×éµØÖ·£¬´ýдÈ볤¶È£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© //Sends a string of data (Array, length, command to be written , SlaveDevice address ). // len = uint8 (max buffer to write 256bytes) static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { @@ -393,7 +419,6 @@ static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint return true; } -// ¶Á³ö1´®Êý¾Ý£¨´æ·Å¶Á³öÊý¾Ý£¬´ý¶Á³ö³¤¶È£¬´ø¶Á³öµØÖ·£¬Æ÷¼þÀàÐÍ£© // read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ). // len = uint8 (max buffer to read 256bytes) static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { @@ -403,7 +428,7 @@ static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, ui // extra wait 500us (514us measured) // 200us (xx measured) - SpinDelayUs(600); + WaitUS(600); bool bBreak = true; uint16_t readcount = 0; @@ -462,6 +487,7 @@ static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, ui } I2C_Stop(); + // return bytecount - first byte (which is length byte) return --readcount; } @@ -481,12 +507,10 @@ static int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, if (!I2C_WaitAck()) break; - // msb I2C_SendByte(msb); if (!I2C_WaitAck()) break; - // lsb I2C_SendByte(lsb); if (!I2C_WaitAck()) break; @@ -543,12 +567,10 @@ static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, ui if (!I2C_WaitAck()) break; - // msb I2C_SendByte(msb); if (!I2C_WaitAck()) break; - // lsb I2C_SendByte(lsb); if (!I2C_WaitAck()) break; @@ -577,7 +599,6 @@ static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, ui void I2C_print_status(void) { DbpString("Smart card module (ISO 7816)"); uint8_t resp[] = {0,0,0,0}; - I2C_init(); I2C_Reset_EnterMainProgram(); uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN); if ( len > 0 ) @@ -624,8 +645,6 @@ static bool GetATR(smart_card_atr_t *card_ptr) { // Send ATR // start [C0 01] stop start C1 len aa bb cc stop] I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN); - uint8_t cmd[1] = {1}; - LogTrace(cmd, 1, 0, 0, NULL, true); // wait for sim card to answer. // 1byte = 1ms, max frame 256bytes. Should wait 256ms at least just in case. @@ -659,19 +678,6 @@ static bool GetATR(smart_card_atr_t *card_ptr) { } } - // for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr - if (len == 1) { - - uint8_t data[1] = {0}; - I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); - - if ( !I2C_WaitForSim() ) - return false; - - uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); - len = len + len2; - } - card_ptr->atr_len = len; LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); @@ -683,7 +689,6 @@ void SmartCardAtr(void) { LED_D_ON(); clear_trace(); set_tracing(true); - I2C_init(); I2C_Reset_EnterMainProgram(); bool isOK = GetATR( &card ); cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); @@ -706,10 +711,9 @@ void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) { if ((flags & SC_CONNECT)) { - I2C_init(); I2C_Reset_EnterMainProgram(); - if ( !(flags & SC_NO_SELECT) ) { + if (flags & SC_SELECT) { smart_card_atr_t card; bool gotATR = GetATR( &card ); //cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); @@ -725,18 +729,21 @@ void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) { // Send raw bytes // asBytes = A0 A4 00 00 02 // arg1 = len 5 - I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); - - if ( !I2C_WaitForSim() ) - goto OUT; + bool res = I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + if ( !res && MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); // read bytes from module len = ISO7618_MAX_FRAME; - sc_rx_bytes(resp, &len); - LogTrace(resp, len, 0, 0, NULL, false); + res = sc_rx_bytes(resp, &len); + if ( res ) { + LogTrace(resp, len, 0, 0, NULL, false); + } else { + len = 0; + } } OUT: cmd_send(CMD_ACK, len, 0, 0, resp, len); + BigBuf_free(); set_tracing(false); LEDsoff(); } @@ -749,7 +756,6 @@ void SmartCardUpgrade(uint64_t arg0) { // write. Sector0, with 11,22,33,44 // erase is 128bytes, and takes 50ms to execute - I2C_init(); I2C_Reset_EnterBootloader(); bool isOK = true; @@ -777,7 +783,7 @@ void SmartCardUpgrade(uint64_t arg0) { } // writing takes time. - SpinDelay(50); + WaitMS(50); // read res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); @@ -799,6 +805,7 @@ void SmartCardUpgrade(uint64_t arg0) { } cmd_send(CMD_ACK, isOK, pos, 0, 0, 0); LED_C_OFF(); + BigBuf_free(); } // unfinished (or not needed?) @@ -808,7 +815,6 @@ void SmartCardUpgrade(uint64_t arg0) { void SmartCardSetClock(uint64_t arg0) { LED_D_ON(); set_tracing(true); - I2C_init(); I2C_Reset_EnterMainProgram(); // Send SIM CLC diff --git a/client/cmdhf.c b/client/cmdhf.c index d2201800..56b1cb3d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -415,6 +415,8 @@ int CmdHFList(const char *Cmd) protocol = ISO_14443B; } else if(strcmp(type,"topaz") == 0) { protocol = TOPAZ; + } else if(strcmp(type, "7816") == 0) { + protocol = ISO_7816_4; } else if(strcmp(type,"raw") == 0) { protocol = -1; //No crc, no annotations } else if (strcmp(type, "save") == 0) { @@ -592,7 +594,6 @@ static command_t CommandTable[] = {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, - {"emv", CmdHFEMV, 1, "{ EMV cards... }"}, {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, diff --git a/client/cmdmain.c b/client/cmdmain.c index f503021a..dbaef70e 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -26,6 +26,7 @@ #include "util.h" #include "util_posix.h" #include "cmdscript.h" +#include "emv/cmdemv.h" // EMV #ifdef WITH_SMARTCARD #include "cmdsmartcard.h" #endif @@ -36,18 +37,21 @@ static int CmdQuit(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help. Use ' help' for details of a particular command."}, - {"data", CmdData, 1, "{ Plot window / data buffer manipulation... }"}, - {"hf", CmdHF, 1, "{ High Frequency commands... }"}, - {"hw", CmdHW, 1, "{ Hardware commands... }"}, - {"lf", CmdLF, 1, "{ Low Frequency commands... }"}, + {"help", CmdHelp, 1, "This help. Use ' help' for details of a particular command."}, + {"data", CmdData, 1, "{ Plot window / data buffer manipulation... }"}, + {"hf", CmdHF, 1, "{ High Frequency commands... }"}, + {"hw", CmdHW, 1, "{ Hardware commands... }"}, + {"lf", CmdLF, 1, "{ Low Frequency commands... }"}, #ifdef WITH_SMARTCARD - {"sc", CmdSmartcard,1,"{ Smartcard commands... }"}, + {"emv", CmdEMV, 1, "{ EMV iso14443 and iso7816... }"}, + {"sc", CmdSmartcard,1,"{ Smartcard commands... }"}, +#else + {"emv", CmdEMV, 1, "{ EMV iso14443 }"}, #endif - {"script",CmdScript,1, "{ Scripting commands }"}, - {"quit", CmdQuit, 1, "Exit program"}, - {"exit", CmdQuit, 1, "Exit program"}, - {NULL, NULL, 0, NULL} + {"script",CmdScript,1, "{ Scripting commands }"}, + {"quit", CmdQuit, 1, "Exit program"}, + {"exit", CmdQuit, 1, "Exit program"}, + {NULL, NULL, 0, NULL} }; command_t* getTopLevelCommandTable() diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c index b2a5705d..4258989c 100644 --- a/client/cmdsmartcard.c +++ b/client/cmdsmartcard.c @@ -8,66 +8,161 @@ // Proxmark3 RDV40 Smartcard module commands //----------------------------------------------------------------------------- #include "cmdsmartcard.h" + +#include + +#include "ui.h" +#include "cmdparser.h" +#include "util.h" #include "smartcard.h" #include "comms.h" #include "protocols.h" +#include "cmdhf.h" // CmdHFlist +#include "emv/apduinfo.h" // APDUcode description +#include "emv/emvcore.h" // decodeTVL static int CmdHelp(const char *Cmd); -int usage_sm_raw(void) { - PrintAndLog("Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>"); - PrintAndLog(" h : this help"); - PrintAndLog(" r : do not read response"); - PrintAndLog(" a : active signal field ON without select"); - PrintAndLog(" s : active signal field ON with select"); - PrintAndLog(" t : executes TLV decoder if it is possible"); - PrintAndLog(" d : bytes to send"); - PrintAndLog(""); - PrintAndLog("Examples:"); - PrintAndLog(" sc raw d 11223344"); +static int usage_sm_raw(void) { + PrintAndLogEx(NORMAL, "Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " r : do not read response"); + PrintAndLogEx(NORMAL, " a : active smartcard without select"); + PrintAndLogEx(NORMAL, " s : active smartcard with select"); + PrintAndLogEx(NORMAL, " t : executes TLV decoder if it possible"); + PrintAndLogEx(NORMAL, " d : bytes to send"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " sc raw d 00a404000e315041592e5359532e444446303100 - `1PAY.SYS.DDF01` PPSE directory"); + PrintAndLogEx(NORMAL, " sc raw d 00a404000e325041592e5359532e444446303100 - `2PAY.SYS.DDF01` PPSE directory"); return 0; } -int usage_sm_reader(void) { - PrintAndLog("Usage: sc reader [h|s]"); - PrintAndLog(" h : this help"); - PrintAndLog(" s : silent (no messages)"); - PrintAndLog(""); - PrintAndLog("Examples:"); - PrintAndLog(" sc reader"); + +static int usage_sm_reader(void) { + PrintAndLogEx(NORMAL, "Usage: sc reader [h|s]"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " s : silent (no messages)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " sc reader"); return 0; } -int usage_sm_info(void) { - PrintAndLog("Usage: sc info [h|s]"); - PrintAndLog(" h : this help"); - PrintAndLog(" s : silent (no messages)"); - PrintAndLog(""); - PrintAndLog("Examples:"); - PrintAndLog(" sc info"); + +static int usage_sm_info(void) { + PrintAndLogEx(NORMAL, "Usage: s info [h|s]"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " s : silent (no messages)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " sc info"); return 0; } -int usage_sm_upgrade(void) { - PrintAndLog("Upgrade firmware"); - PrintAndLog("Usage: sc upgrade f "); - PrintAndLog(" h : this help"); - PrintAndLog(" f : firmware file name"); - PrintAndLog(""); - PrintAndLog("Examples:"); - PrintAndLog(" sc upgrade f myfile"); - PrintAndLog(""); - PrintAndLog("WARNING - Dangerous command, do wrong and you will brick the smart card socket"); + +static int usage_sm_upgrade(void) { + PrintAndLogEx(NORMAL, "Upgrade firmware"); + PrintAndLogEx(NORMAL, "Usage: sc upgrade f "); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " f : firmware file name"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " sc upgrade f myfile"); return 0; } -int usage_sm_setclock(void) { - PrintAndLog("Usage: sc setclock [h] c "); - PrintAndLog(" h : this help"); - PrintAndLog(" c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) "); - PrintAndLog(""); - PrintAndLog("Examples:"); - PrintAndLog(" sc setclock c 2"); + +static int usage_sm_setclock(void) { + PrintAndLogEx(NORMAL, "Usage: sc setclock [h] c "); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " sc setclock c 2"); return 0; } +static int usage_sm_brute(void) { + PrintAndLogEx(NORMAL, "Tries to bruteforce SFI, "); + PrintAndLogEx(NORMAL, "Usage: sc brute [h]"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " sc brute"); + return 0; +} + +static bool smart_select(bool silent) { + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); + return false; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); + return false; + } + + if (!silent) { + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + + PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); + } + + return true; +} + +static int smart_wait(uint8_t *data) { + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + PrintAndLogEx(WARNING, "smart card response failed"); + return -1; + } + + uint32_t len = resp.arg[0]; + if ( !len ) { + PrintAndLogEx(WARNING, "smart card response failed"); + return -2; + } + memcpy(data, resp.d.asBytes, len); + PrintAndLogEx(SUCCESS, " %d | %s", len, sprint_hex_inrow_ex(data, len, 32)); + + if (len >= 2) { + PrintAndLogEx(SUCCESS, "%02X%02X | %s", data[len - 2], data[len - 1], GetAPDUCodeDescription(data[len - 2], data[len - 1])); + } + return len; +} + +static int smart_response(uint8_t *data) { + + int len = -1; + int datalen = smart_wait(data); + + if ( data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F ) { + len = data[datalen - 1]; + } + + if (len == -1 ) { + 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); + + datalen = smart_wait(data); +out: + + return datalen; +} + int CmdSmartRaw(const char *Cmd) { int hexlen = 0; @@ -99,13 +194,13 @@ int CmdSmartRaw(const char *Cmd) { case 'd': { switch (param_gethex_to_eol(Cmd, cmdp+1, data, sizeof(data), &hexlen)) { case 1: - PrintAndLog("Invalid HEX value."); + PrintAndLogEx(WARNING, "Invalid HEX value."); return 1; case 2: - PrintAndLog("Too many bytes. Max %d bytes", sizeof(data)); + PrintAndLogEx(WARNING, "Too many bytes. Max %d bytes", sizeof(data)); return 1; case 3: - PrintAndLog("Hex must have an even number of digits."); + PrintAndLogEx(WARNING, "Hex must have even number of digits."); return 1; } cmdp++; @@ -113,7 +208,7 @@ int CmdSmartRaw(const char *Cmd) { break; } default: - PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; } @@ -130,13 +225,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) - c.arg[0] |= SC_NO_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 ); @@ -145,45 +240,67 @@ int CmdSmartRaw(const char *Cmd) { // reading response from smart card if ( reply ) { - UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - PrintAndLog("smart card response failed"); + + uint8_t* buf = calloc(USB_CMD_DATA_SIZE, sizeof(uint8_t)); + if ( !buf ) return 1; + + int len = smart_response(buf); + if ( len < 0 ) { + free(buf); + return 2; } - uint32_t datalen = resp.arg[0]; - if ( !datalen ) { - PrintAndLog("smart card response failed"); - return 1; + if ( buf[0] == 0x6C ) { + data[4] = buf[1]; + + memcpy(c.d.asBytes, data, sizeof(data) ); + clearCommandBuffer(); + SendCommand(&c); + len = smart_response(buf); + + data[4] = 0; } - PrintAndLog("received %i bytes", datalen); + if (decodeTLV && len > 4) + TLVPrintFromBuffer(buf+1, len-3); - if (!datalen) - return 1; + free(buf); + } + return 0; +} - uint8_t *data = resp.d.asBytes; +int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + *dataoutlen = 0; - // TLV decoder - if (decodeTLV ) { + if (activateCard) + smart_select(false); + printf("* APDU SC\n"); - if (datalen >= 2) { - PrintAndLog("%02x %02x | %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); - } - if (datalen > 4) { - TLVPrintFromBuffer(data, datalen - 2); - } - } else { - PrintAndLog("%s", sprint_hex(data, datalen)); - } + UsbCommand c = {CMD_SMART_RAW, {SC_RAW | SC_CONNECT, datainlen, 0}}; + if (activateCard) { + c.arg[0] |= SC_SELECT; } + memcpy(c.d.asBytes, datain, datainlen); + clearCommandBuffer(); + SendCommand(&c); + + int len = smart_response(dataout); + + if ( len < 0 ) { + return 2; + } + + *dataoutlen = len; + return 0; } + int CmdSmartUpgrade(const char *Cmd) { - PrintAndLog("WARNING - Smartcard socket firmware upgrade."); - PrintAndLog("Dangerous command, do wrong and you will brick the smart card socket"); + PrintAndLogEx(WARNING, "WARNING - Smartcard socket firmware upgrade."); + PrintAndLogEx(WARNING, "A dangerous command, do wrong and you will brick the smart card socket"); FILE *f; char filename[FILE_PATH_SIZE] = {0}; @@ -195,7 +312,7 @@ int CmdSmartUpgrade(const char *Cmd) { case 'f': //File handling and reading if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) { - PrintAndLog("Filename too long"); + PrintAndLogEx(FAILED, "Filename too long"); errors = true; break; } @@ -204,7 +321,7 @@ int CmdSmartUpgrade(const char *Cmd) { case 'h': return usage_sm_upgrade(); default: - PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; } @@ -215,8 +332,8 @@ int CmdSmartUpgrade(const char *Cmd) { // load file f = fopen(filename, "rb"); - if ( !f ) { - PrintAndLog("File: %s: not found or locked.", filename); + if ( !f ){ + PrintAndLogEx(FAILED, "File: %s: not found or locked.", filename); return 1; } @@ -225,15 +342,15 @@ int CmdSmartUpgrade(const char *Cmd) { long fsize = ftell(f); fseek(f, 0, SEEK_SET); - if (fsize < 0) { - PrintAndLog("error, when getting filesize"); + if (fsize < 0) { + PrintAndLogEx(WARNING, "error, when getting filesize"); fclose(f); return 1; } - + uint8_t *dump = calloc(fsize, sizeof(uint8_t)); if (!dump) { - PrintAndLog("error, cannot allocate memory "); + PrintAndLogEx(WARNING, "error, cannot allocate memory "); fclose(f); return 1; } @@ -242,7 +359,7 @@ int CmdSmartUpgrade(const char *Cmd) { if (f) fclose(f); - PrintAndLog("Smartcard socket firmware uploading to PM3"); + PrintAndLogEx(SUCCESS, "Smartcard socket firmware uploading to PM3"); //Send to device uint32_t index = 0; uint32_t bytes_sent = 0; @@ -258,7 +375,7 @@ int CmdSmartUpgrade(const char *Cmd) { clearCommandBuffer(); SendCommand(&c); if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2000) ) { - PrintAndLog("timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply."); free(dump); return 1; } @@ -269,7 +386,7 @@ int CmdSmartUpgrade(const char *Cmd) { } free(dump); printf("\n"); - PrintAndLog("Smartcard socket firmware updating, don\'t turn off your PM3!"); + PrintAndLogEx(SUCCESS, "Smartcard socket firmware updating, don\'t turn off your PM3!"); // trigger the firmware upgrade UsbCommand c = {CMD_SMART_UPGRADE, {bytes_read, 0, 0}}; @@ -277,13 +394,13 @@ int CmdSmartUpgrade(const char *Cmd) { SendCommand(&c); UsbCommand resp; if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { - PrintAndLog("timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply."); return 1; } - if ( (resp.arg[0] && 0xFF ) ) - PrintAndLog("Smartcard socket firmware upgraded successful"); + if ( (resp.arg[0] & 0xFF ) ) + PrintAndLogEx(SUCCESS, "Smartcard socket firmware upgraded successful"); else - PrintAndLog("Smartcard socket firmware updating failed"); + PrintAndLogEx(FAILED, "Smartcard socket firmware updating failed"); return 0; } @@ -294,11 +411,11 @@ int CmdSmartInfo(const char *Cmd){ while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': return usage_sm_info(); - case 's': + case 's': silent = true; break; default: - PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; } @@ -313,13 +430,13 @@ int CmdSmartInfo(const char *Cmd){ SendCommand(&c); UsbCommand resp; if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { - if (!silent) PrintAndLog("smart card select failed"); + if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); return 1; } uint8_t isok = resp.arg[0] & 0xFF; if (!isok) { - if (!silent) PrintAndLog("smart card select failed"); + if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); return 1; } @@ -327,11 +444,11 @@ int CmdSmartInfo(const char *Cmd){ memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); // print header - PrintAndLog("\n--- Smartcard Information ---------"); - PrintAndLog("-------------------------------------------------------------"); - PrintAndLog("ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len)); - PrintAndLog("look up ATR"); - PrintAndLog("http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) ); + PrintAndLogEx(INFO, "\n--- Smartcard Information ---------"); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + 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) ); return 0; } @@ -342,11 +459,11 @@ int CmdSmartReader(const char *Cmd){ while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': return usage_sm_reader(); - case 's': + case 's': silent = true; break; default: - PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; } @@ -361,18 +478,19 @@ int CmdSmartReader(const char *Cmd){ SendCommand(&c); UsbCommand resp; if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { - if (!silent) PrintAndLog("smart card select failed"); + if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); return 1; } uint8_t isok = resp.arg[0] & 0xFF; if (!isok) { - if (!silent) PrintAndLog("smart card select failed"); + if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); return 1; } smart_card_atr_t card; memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); - PrintAndLog("ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); + + PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); return 0; } @@ -383,15 +501,15 @@ int CmdSmartSetClock(const char *Cmd){ while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': return usage_sm_setclock(); - case 'c': + case 'c': clock = param_get8ex(Cmd, cmdp+1, 2, 10); if ( clock > 2) errors = true; - + cmdp += 2; break; default: - PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; } @@ -405,25 +523,25 @@ int CmdSmartSetClock(const char *Cmd){ SendCommand(&c); UsbCommand resp; if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { - PrintAndLog("smart card select failed"); + PrintAndLogEx(WARNING, "smart card select failed"); return 1; } uint8_t isok = resp.arg[0] & 0xFF; if (!isok) { - PrintAndLog("smart card set clock failed"); + PrintAndLogEx(WARNING, "smart card set clock failed"); return 1; } switch (clock) { case 0: - PrintAndLog("Clock changed to 16mhz giving 10800 baudrate"); + PrintAndLogEx(SUCCESS, "Clock changed to 16mhz giving 10800 baudrate"); break; case 1: - PrintAndLog("Clock changed to 8mhz giving 21600 baudrate"); + PrintAndLogEx(SUCCESS, "Clock changed to 8mhz giving 21600 baudrate"); break; case 2: - PrintAndLog("Clock changed to 4mhz giving 86400 baudrate"); + PrintAndLogEx(SUCCESS, "Clock changed to 4mhz giving 86400 baudrate"); break; default: break; @@ -431,267 +549,77 @@ int CmdSmartSetClock(const char *Cmd){ return 0; } - -// iso 7816-3 -void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ - // S-block - if ( (cmd[0] & 0xC0) && (cmdsize == 3) ) { - switch ( (cmd[0] & 0x3f) ) { - case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break; - case 0x20 : snprintf(exp, size, "S-block RESYNCH resp"); break; - case 0x01 : snprintf(exp, size, "S-block IFS req"); break; - case 0x21 : snprintf(exp, size, "S-block IFS resp"); break; - case 0x02 : snprintf(exp, size, "S-block ABORT req"); break; - case 0x22 : snprintf(exp, size, "S-block ABORT resp"); break; - case 0x03 : snprintf(exp, size, "S-block WTX reqt"); break; - case 0x23 : snprintf(exp, size, "S-block WTX resp"); break; - default : snprintf(exp, size, "S-block"); break; - } - } - // R-block (ack) - else if ( ((cmd[0] & 0xD0) == 0x80) && ( cmdsize > 2) ) { - if ( (cmd[0] & 0x10) == 0 ) - snprintf(exp, size, "R-block ACK"); - else - snprintf(exp, size, "R-block NACK"); - } - // I-block - else { - - int pos = (cmd[0] == 2 || cmd[0] == 3) ? 2 : 3; - switch ( cmd[pos] ) { - case ISO7816_READ_BINARY :snprintf(exp, size, "READ BIN");break; - case ISO7816_WRITE_BINARY :snprintf(exp, size, "WRITE BIN");break; - case ISO7816_UPDATE_BINARY :snprintf(exp, size, "UPDATE BIN");break; - case ISO7816_ERASE_BINARY :snprintf(exp, size, "ERASE BIN");break; - case ISO7816_READ_RECORDS :snprintf(exp, size, "READ RECORDS");break; - case ISO7816_WRITE_RECORDS :snprintf(exp, size, "WRITE RECORDS");break; - case ISO7816_APPEND_RECORD :snprintf(exp, size, "APPEND RECORD");break; - case ISO7816_UPDATE_RECORD :snprintf(exp, size, "UPDATE RECORD");break; - case ISO7816_GET_DATA :snprintf(exp, size, "GET DATA");break; - case ISO7816_PUT_DATA :snprintf(exp, size, "PUT DATA");break; - case ISO7816_SELECT_FILE :snprintf(exp, size, "SELECT FILE");break; - case ISO7816_VERIFY :snprintf(exp, size, "VERIFY");break; - case ISO7816_INTERNAL_AUTHENTICATION :snprintf(exp, size, "INTERNAL AUTH");break; - case ISO7816_EXTERNAL_AUTHENTICATION :snprintf(exp, size, "EXTERNAL AUTH");break; - case ISO7816_GET_CHALLENGE :snprintf(exp, size, "GET CHALLENGE");break; - case ISO7816_MANAGE_CHANNEL :snprintf(exp, size, "MANAGE CHANNEL");break; - default :snprintf(exp, size, "?"); break; - } - } +int CmdSmartList(const char *Cmd) { + CmdHFList("7816"); + return 0; } +int CmdSmartBruteforceSFI(const char *Cmd) { -uint16_t printScTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) { - // sanity check - if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; - - bool isResponse; - uint16_t data_len, parity_len; - uint32_t duration, timestamp, first_timestamp, EndOfTransmissionTimestamp; - char explanation[30] = {0}; - - first_timestamp = *((uint32_t *)(trace)); - timestamp = *((uint32_t *)(trace + tracepos)); - tracepos += 4; - - duration = *((uint16_t *)(trace + tracepos)); - tracepos += 2; - - data_len = *((uint16_t *)(trace + tracepos)); - tracepos += 2; - - if (data_len & 0x8000) { - data_len &= 0x7fff; - isResponse = true; - } else { - isResponse = false; - } - - parity_len = (data_len-1)/8 + 1; - if (tracepos + data_len + parity_len > traceLen) { - return traceLen; - } - uint8_t *frame = trace + tracepos; - tracepos += data_len; - //uint8_t *parityBytes = trace + tracepos; - tracepos += parity_len; - - //--- Draw the data column - char line[18][110]; + char ctmp = tolower(param_getchar(Cmd, 0)); + if (ctmp == 'h') return usage_sm_brute(); - if (data_len == 0 ) { - sprintf(line[0],""); - return tracepos; - } + uint8_t data[5] = {0x00, 0xB2, 0x00, 0x00, 0x00}; - for (int j = 0; j < data_len && j/18 < 18; j++) { - snprintf(line[j/18]+(( j % 18) * 4),110, "%02x ", frame[j]); + PrintAndLogEx(INFO, "Selecting card"); + if ( !smart_select(false) ) { + return 1; } - EndOfTransmissionTimestamp = timestamp + duration; - - annotateIso7816(explanation,sizeof(explanation),frame,data_len); - - int num_lines = MIN((data_len - 1)/18 + 1, 18); - for (int j = 0; j < num_lines ; j++) { - if (j == 0) { - PrintAndLog(" %10u | %10u | %s |%-72s | %s| %s", - (timestamp - first_timestamp), - (EndOfTransmissionTimestamp - first_timestamp), - (isResponse ? "Tag" : "Rdr"), - line[j], - " ", - (j == num_lines-1) ? explanation : ""); - } else { - PrintAndLog(" | | |%-72s | %s| %s", - line[j], - " ", - (j == num_lines-1) ? explanation : ""); - } - } + PrintAndLogEx(INFO, "Selecting PPSE aid"); + CmdSmartRaw("d 00a404000e325041592e5359532e444446303100"); + CmdSmartRaw("d 00a4040007a000000004101000"); - // if is last record - if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen) return traceLen; + PrintAndLogEx(INFO, "starting"); - return tracepos; -} + UsbCommand c = {CMD_SMART_RAW, {SC_RAW, sizeof(data), 0}}; + uint8_t* buf = malloc(USB_CMD_DATA_SIZE); + if ( !buf ) + return 1; -int ScTraceList(const char *Cmd) { - bool loadFromFile = false; - bool saveToFile = false; - char type[5] = {0}; - char filename[FILE_PATH_SIZE] = {0}; + for (uint8_t i=1; i < 4; i++) { + for (int p1=1; p1 < 5; p1++) { - // parse command line - param_getstr(Cmd, 0, type, sizeof(type)); - param_getstr(Cmd, 1, filename, sizeof(filename)); + data[2] = p1; + data[3] = (i << 3) + 4; - bool errors = false; - if(type[0] == 'h') { - errors = true; - } + memcpy(c.d.asBytes, data, sizeof(data) ); + clearCommandBuffer(); + SendCommand(&c); - if(!errors) { - if (strcmp(type, "s") == 0) { - saveToFile = true; - } else if (strcmp(type,"l") == 0) { - loadFromFile = true; - } - } + smart_response(buf); - if ((loadFromFile || saveToFile) && strlen(filename) == 0) { - errors = true; - } + // if 0x6C + if ( buf[0] == 0x6C ) { + data[4] = buf[1]; - if (loadFromFile && saveToFile) { - errors = true; - } + memcpy(c.d.asBytes, data, sizeof(data) ); + clearCommandBuffer(); + SendCommand(&c); + uint8_t len = smart_response(buf); - if (errors) { - PrintAndLog("List or save protocol data."); - PrintAndLog("Usage: sc list [l ]"); - PrintAndLog(" sc list [s ]"); - PrintAndLog(" l - load data from file instead of trace buffer"); - PrintAndLog(" s - save data to file"); - PrintAndLog(""); - PrintAndLog("example: sc list"); - PrintAndLog("example: sc list save myCardTrace.trc"); - PrintAndLog("example: sc list l myCardTrace.trc"); - return 0; - } + // TLV decoder + if (len > 4) + TLVPrintFromBuffer(buf+1, len-3); - uint8_t *trace; - uint32_t tracepos = 0; - uint32_t traceLen = 0; - - if (loadFromFile) { - #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions - FILE *tracefile = NULL; - size_t bytes_read; - trace = malloc(TRACE_CHUNK_SIZE); - if (trace == NULL) { - PrintAndLog("Cannot allocate memory for trace"); - return 2; - } - if ((tracefile = fopen(filename,"rb")) == NULL) { - PrintAndLog("Could not open file %s", filename); - free(trace); - return 0; - } - while (!feof(tracefile)) { - bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile); - traceLen += bytes_read; - if (!feof(tracefile)) { - uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE); - if (p == NULL) { - PrintAndLog("Cannot allocate memory for trace"); - free(trace); - fclose(tracefile); - return 2; - } - trace = p; - } - } - fclose(tracefile); - } else { - trace = malloc(USB_CMD_DATA_SIZE); - // Query for the size of the trace - UsbCommand response; - GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); - traceLen = response.arg[2]; - if (traceLen > USB_CMD_DATA_SIZE) { - uint8_t *p = realloc(trace, traceLen); - if (p == NULL) { - PrintAndLog("Cannot allocate memory for trace"); - free(trace); - return 2; + data[4] = 0; } - trace = p; - GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); - } - } - - if (saveToFile) { - FILE *tracefile = NULL; - if ((tracefile = fopen(filename,"wb")) == NULL) { - PrintAndLog("Could not create file %s", filename); - return 1; - } - fwrite(trace, 1, traceLen, tracefile); - PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); - fclose(tracefile); - } else { - PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); - PrintAndLog(""); - PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); - PrintAndLog(""); - PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); - PrintAndLog("------------|------------|-----|-------------------------------------------------------------------------|-----|--------------------|"); - - while(tracepos < traceLen) - { - tracepos = printScTraceLine(tracepos, traceLen, trace); + memset(buf, 0x00, USB_CMD_DATA_SIZE); } } - - free(trace); - return 0; -} - -int CmdSmartList(const char *Cmd) { - ScTraceList(Cmd); + free(buf); return 0; } static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"list", CmdSmartList, 0, "List ISO 7816 history"}, - {"info", CmdSmartInfo, 1, "Tag information [rdv40]"}, - {"reader", CmdSmartReader, 1, "Act like an IS07816 reader [rdv40]"}, - {"raw", CmdSmartRaw, 1, "Send raw hex data to tag [rdv40]"}, - {"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware [rdv40]"}, - {"setclock",CmdSmartSetClock, 1, "Set clock speed"}, + {"help", CmdHelp, 1, "This help"}, + {"list", CmdSmartList, 0, "List ISO 7816 history"}, + {"info", CmdSmartInfo, 1, "Tag information"}, + {"reader", CmdSmartReader, 1, "Act like an IS07816 reader"}, + {"raw", CmdSmartRaw, 1, "Send raw hex data to tag"}, + {"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware"}, + {"setclock", CmdSmartSetClock, 1, "Set clock speed"}, + {"brute", CmdSmartBruteforceSFI, 1, "Bruteforce SFI"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdsmartcard.h b/client/cmdsmartcard.h index caa06f4f..3a4c7956 100644 --- a/client/cmdsmartcard.h +++ b/client/cmdsmartcard.h @@ -11,19 +11,8 @@ #ifndef CMDSMARTCARD_H__ #define CMDSMARTCARD_H__ -#include -#include -#include -#include -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" -#include "common.h" -#include "util.h" -#include "loclass/fileutils.h" // saveFile -#include "cmdmain.h" // getfromdevice -#include "emv/emvcore.h" // decodeTVL -#include "emv/apduinfo.h" // APDUcode description +#include +#include extern int CmdSmartcard(const char *Cmd); @@ -32,8 +21,6 @@ extern int CmdSmartUpgrade(const char* cmd); extern int CmdSmartInfo(const char* cmd); extern int CmdSmartReader(const char *Cmd); -extern int usage_sm_raw(void); -extern int usage_sm_reader(void); -extern int usage_sm_info(void); -extern int usage_sm_upgrade(void); +extern int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); + #endif diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index d886834d..cb42e8f7 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -38,13 +38,13 @@ void ParamLoadDefaults(struct tlvdb *tlvRoot) { TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC } -int CmdHFEMVSelect(const char *cmd) { +int CmdEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv select", + CLIParserInit("emv select", "Executes select applet command", - "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); + "Usage:\n\temv select -s a00000000101 -> select card, select applet\n\temv select -st a00000000101 -> select card, select applet, show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -52,6 +52,9 @@ int CmdHFEMVSelect(const char *cmd) { arg_lit0("kK", "keep", "keep field for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx0(NULL, NULL, "", NULL), arg_param_end }; @@ -61,7 +64,14 @@ int CmdHFEMVSelect(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(5)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(6, data, &datalen); +#else CLIGetHexWithReturn(5, data, &datalen); +#endif CLIParserFree(); SetAPDULogging(APDULogging); @@ -70,10 +80,10 @@ int CmdHFEMVSelect(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + int res = EMVSelect(channel, activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -84,11 +94,11 @@ int CmdHFEMVSelect(const char *cmd) { return 0; } -int CmdHFEMVSearch(const char *cmd) { +int CmdEMVSearch(const char *cmd) { - CLIParserInit("hf emv search", + CLIParserInit("emv search", "Tries to select all applets from applet list:\n", - "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); + "Usage:\n\temv search -s -> select card and search\n\temv search -st -> select card, search and show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -96,6 +106,9 @@ int CmdHFEMVSearch(const char *cmd) { arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -104,6 +117,11 @@ int CmdHFEMVSearch(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(5)) + channel = ECC_CONTACT; +#endif CLIParserFree(); SetAPDULogging(APDULogging); @@ -112,12 +130,12 @@ int CmdHFEMVSearch(const char *cmd) { const char *al = "Applets list"; t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); - if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) { + if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t)) { tlvdb_free(t); return 2; } - PrintAndLog("Search completed."); + PrintAndLogEx(SUCCESS, "Search completed."); // print list here if (!decodeTLV) { @@ -129,11 +147,11 @@ int CmdHFEMVSearch(const char *cmd) { return 0; } -int CmdHFEMVPPSE(const char *cmd) { +int CmdEMVPPSE(const char *cmd) { - CLIParserInit("hf emv pse", + CLIParserInit("emv pse", "Executes PSE/PPSE select command. It returns list of applet on the card:\n", - "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); + "Usage:\n\temv pse -s1 -> select, get pse\n\temv pse -st2 -> select, get ppse, show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -143,6 +161,9 @@ int CmdHFEMVPPSE(const char *cmd) { arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -156,7 +177,12 @@ int CmdHFEMVPPSE(const char *cmd) { PSENum = 2; bool APDULogging = arg_get_lit(5); bool decodeTLV = arg_get_lit(6); - CLIParserFree(); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(7)) + channel = ECC_CONTACT; +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -164,10 +190,10 @@ int CmdHFEMVPPSE(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); + int res = EMVSelectPSE(channel, activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -179,15 +205,15 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } -int CmdHFEMVGPO(const char *cmd) { +int CmdEMVGPO(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv gpo", + CLIParserInit("emv gpo", "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", - "Usage:\n\thf emv gpo -k -> execute GPO\n" - "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" - "\thf emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); + "Usage:\n\temv gpo -k -> execute GPO\n" + "\temv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" + "\temv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -196,6 +222,9 @@ int CmdHFEMVGPO(const char *cmd) { arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx0(NULL, NULL, "", NULL), arg_param_end }; @@ -206,8 +235,15 @@ int CmdHFEMVGPO(const char *cmd) { bool dataMakeFromPDOL = arg_get_lit(3); bool APDULogging = arg_get_lit(4); bool decodeTLV = arg_get_lit(5); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(6)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(7, data, &datalen); +#else CLIGetHexWithReturn(6, data, &datalen); - CLIParserFree(); +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -226,19 +262,19 @@ int CmdHFEMVGPO(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "Params loading from file..."); ParamLoadFromJson(tlvRoot); }; pdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f38, datalen, data), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("ERROR: can't create PDOL TLV."); + PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); return 4; } } else { if (paramsLoadFromFile) { - PrintAndLog("WARNING: don't need to load parameters. Sending plain PDOL data..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain PDOL data..."); } pdol_data_tlv = &data_tlv; } @@ -246,24 +282,24 @@ int CmdHFEMVGPO(const char *cmd) { size_t pdol_data_tlv_data_len = 0; unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); if (!pdol_data_tlv_data) { - PrintAndLog("ERROR: can't create PDOL data."); + PrintAndLogEx(ERR, "Can't create PDOL data."); tlvdb_free(tlvRoot); return 4; } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + int res = EMVGPO(channel, leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); if (pdol_data_tlv != &data_tlv) free(pdol_data_tlv); tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -274,19 +310,22 @@ int CmdHFEMVGPO(const char *cmd) { return 0; } -int CmdHFEMVReadRecord(const char *cmd) { +int CmdEMVReadRecord(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv readrec", + CLIParserInit("emv readrec", "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", - "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); + "Usage:\n\temv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\temv readrec -kt 0201-> read file 0201 and show result in TLV\n"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx1(NULL, NULL, "", NULL), arg_param_end }; @@ -295,11 +334,18 @@ int CmdHFEMVReadRecord(const char *cmd) { bool leaveSignalON = arg_get_lit(1); bool APDULogging = arg_get_lit(2); bool decodeTLV = arg_get_lit(3); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(4)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(5, data, &datalen); +#else CLIGetHexWithReturn(4, data, &datalen); +#endif CLIParserFree(); if (datalen != 2) { - PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + PrintAndLogEx(ERROR, "Command needs to have 2 bytes of data"); return 1; } @@ -309,10 +355,10 @@ int CmdHFEMVReadRecord(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); + int res = EMVReadRecord(channel, leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -324,16 +370,16 @@ int CmdHFEMVReadRecord(const char *cmd) { return 0; } -int CmdHFEMVAC(const char *cmd) { +int CmdEMVAC(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv genac", + CLIParserInit("emv genac", "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" - "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" - "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" - "\thf emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); + "Usage:\n\temv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "\temv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "\temv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" + "\temv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); void* argtable[] = { arg_param_begin, @@ -344,6 +390,9 @@ int CmdHFEMVAC(const char *cmd) { arg_lit0("mM", "make", "make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx1(NULL, NULL, "", NULL), arg_param_end }; @@ -373,8 +422,15 @@ int CmdHFEMVAC(const char *cmd) { bool dataMakeFromCDOL = arg_get_lit(5); bool APDULogging = arg_get_lit(6); bool decodeTLV = arg_get_lit(7); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(8)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(9, data, &datalen); +#else CLIGetHexWithReturn(8, data, &datalen); - CLIParserFree(); +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -394,37 +450,37 @@ int CmdHFEMVAC(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "Params loading from file..."); ParamLoadFromJson(tlvRoot); }; cdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x8c, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol_data_tlv){ - PrintAndLog("ERROR: can't create CDOL TLV."); + PrintAndLogEx(ERR, "Can't create CDOL TLV."); tlvdb_free(tlvRoot); return 4; } } else { if (paramsLoadFromFile) { - PrintAndLog("WARNING: don't need to load parameters. Sending plain CDOL data..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain CDOL data..."); } cdol_data_tlv = &data_tlv; } - PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + PrintAndLogEx(INFO, "CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + int res = EMVAC(channel, leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (cdol_data_tlv != &data_tlv) free(cdol_data_tlv); tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -432,26 +488,34 @@ int CmdHFEMVAC(const char *cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); - return 0; + return 0; } -int CmdHFEMVGenerateChallenge(const char *cmd) { +int CmdEMVGenerateChallenge(const char *cmd) { - CLIParserInit("hf emv challenge", + CLIParserInit("emv challenge", "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); + "Usage:\n\temv challenge -> get challenge\n\temv challenge -k -> get challenge, keep fileld ON\n"); void* argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); bool leaveSignalON = arg_get_lit(1); bool APDULogging = arg_get_lit(2); - CLIParserFree(); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(3)) + channel = ECC_CONTACT; +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -459,31 +523,31 @@ int CmdHFEMVGenerateChallenge(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + int res = EMVGenerateChallenge(channel, leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; - PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + PrintAndLogEx(SUCCESS, "Challenge: %s", sprint_hex(buf, len)); if (len != 4 && len != 8) - PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); + PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, but it %d", len); return 0; } -int CmdHFEMVInternalAuthenticate(const char *cmd) { +int CmdEMVInternalAuthenticate(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv intauth", + CLIParserInit("emv intauth", "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" - "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" - "\thf emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); + "Usage:\n\temv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "\temv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" + "\temv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); void* argtable[] = { arg_param_begin, @@ -492,6 +556,9 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx1(NULL, NULL, "", NULL), arg_param_end }; @@ -502,8 +569,15 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { bool dataMakeFromDDOL = arg_get_lit(3); bool APDULogging = arg_get_lit(4); bool decodeTLV = arg_get_lit(5); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(6)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(7, data, &datalen); +#else CLIGetHexWithReturn(6, data, &datalen); - CLIParserFree(); +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -523,37 +597,37 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "Params loading from file..."); ParamLoadFromJson(tlvRoot); }; ddol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f49, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag if (!ddol_data_tlv){ - PrintAndLog("ERROR: can't create DDOL TLV."); + PrintAndLogEx(ERR, "Can't create DDOL TLV."); tlvdb_free(tlvRoot); return 4; } } else { if (paramsLoadFromFile) { - PrintAndLog("WARNING: don't need to load parameters. Sending plain DDOL data..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain DDOL data..."); } ddol_data_tlv = &data_tlv; } - PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); + PrintAndLogEx(INFO, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + int res = EMVInternalAuthenticate(channel, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); if (ddol_data_tlv != &data_tlv) free(ddol_data_tlv); - tlvdb_free(tlvRoot); + tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -561,7 +635,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); - return 0; + return 0; } #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} @@ -607,13 +681,13 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, } if (len < 4 || (len - 4) % 4) { - PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + PrintAndLogEx(ERR, "GPO response format1 parsing error. length=%d", len); } else { // AIP struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); tlvdb_add(tlvRoot, f1AIP); if (decodeTLV){ - PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); + PrintAndLogEx(INFO, "\n* * Decode response format 1 (0x80) AIP and AFL:"); TLVPrintFromTLV(f1AIP); } @@ -622,14 +696,14 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, tlvdb_add(tlvRoot, f1AFL); if (decodeTLV) TLVPrintFromTLV(f1AFL); - } + } } else { if (decodeTLV) TLVPrintFromBuffer(buf, len); } } -int CmdHFEMVExec(const char *cmd) { +int CmdEMVExec(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -644,10 +718,10 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb *tlvRoot = NULL; struct tlv *pdol_data_tlv = NULL; - CLIParserInit("hf emv exec", + CLIParserInit("emv exec", "Executes EMV contactless transaction", - "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" - "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + "Usage:\n\temv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); void* argtable[] = { arg_param_begin, @@ -661,6 +735,9 @@ int CmdHFEMVExec(const char *cmd) { arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -672,14 +749,19 @@ int CmdHFEMVExec(const char *cmd) { bool forceSearch = arg_get_lit(5); enum TransactionType TrType = TT_MSD; - if (arg_get_lit(6)) - TrType = TT_QVSDCMCHIP; if (arg_get_lit(7)) - TrType = TT_CDA; + TrType = TT_QVSDCMCHIP; if (arg_get_lit(8)) + TrType = TT_CDA; + if (arg_get_lit(9)) TrType = TT_VSDC; - bool GenACGPO = arg_get_lit(9); + bool GenACGPO = arg_get_lit(10); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(11)) + channel = ECC_CONTACT; +#endif CLIParserFree(); SetAPDULogging(showAPDU); @@ -692,12 +774,12 @@ int CmdHFEMVExec(const char *cmd) { // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html if (!forceSearch) { // PPSE - PrintAndLog("\n* PPSE."); + PrintAndLogEx(NORMAL, "\n* PPSE."); SetAPDULogging(showAPDU); - res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); + res = EMVSearchPSE(channel, activateField, true, decodeTLV, tlvSelect); // check PPSE and select application id - if (!res) { + if (!res) { TLVPrintAIDlistFromSelectTLV(tlvSelect); EMVSelectApplication(tlvSelect, AID, &AIDlen); } @@ -705,9 +787,9 @@ int CmdHFEMVExec(const char *cmd) { // Search if (!AIDlen) { - PrintAndLog("\n* Search AID in list."); + PrintAndLogEx(NORMAL, "\n* Search AID in list."); SetAPDULogging(false); - if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { + if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect)) { dreturn(2); } @@ -722,51 +804,51 @@ int CmdHFEMVExec(const char *cmd) { // check if we found EMV application on card if (!AIDlen) { - PrintAndLog("Can't select AID. EMV AID not found"); + PrintAndLogEx(WARNING, "Can't select AID. EMV AID not found"); dreturn(2); } // Select - PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); + PrintAndLogEx(NORMAL, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); SetAPDULogging(showAPDU); - res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); - if (res) { - PrintAndLog("Can't select AID (%d). Exit...", res); + if (res) { + PrintAndLogEx(WARNING, "Can't select AID (%d). Exit...", res); dreturn(3); } if (decodeTLV) TLVPrintFromBuffer(buf, len); - PrintAndLog("* Selected."); + PrintAndLogEx(INFO, "* Selected."); - PrintAndLog("\n* Init transaction parameters."); + PrintAndLogEx(INFO, "\n* Init transaction parameters."); InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); TLVPrintFromTLV(tlvRoot); // TODO delete!!! - PrintAndLog("\n* Calc PDOL."); + PrintAndLogEx(NORMAL, "\n* Calc PDOL."); pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("ERROR: can't create PDOL TLV."); + PrintAndLogEx(WARNING, "Error: can't create PDOL TLV."); dreturn(4); } size_t pdol_data_tlv_data_len; unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); if (!pdol_data_tlv_data) { - PrintAndLog("ERROR: can't create PDOL data."); + PrintAndLogEx(WARNING, "Error: can't create PDOL data."); dreturn(4); } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(NORMAL, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); - PrintAndLog("\n* GPO."); - res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + PrintAndLogEx(NORMAL, "\n* GPO."); + res = EMVGPO(channel, true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); //free(pdol_data_tlv); --- free on exit. - if (res) { - PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + if (res) { + PrintAndLogEx(NORMAL, "GPO error(%d): %4x. Exit...", res, sw); dreturn(5); } @@ -781,23 +863,23 @@ int CmdHFEMVExec(const char *cmd) { if (pan) { tlvdb_add(tlvRoot, pan); - const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); - PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); + const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); + PrintAndLogEx(NORMAL, "\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); } else { - PrintAndLog("\n* * WARNING: Can't extract PAN from track2."); + PrintAndLogEx(NORMAL, "\n* * WARNING: Can't extract PAN from track2."); } } } - PrintAndLog("\n* Read records from AFL."); + PrintAndLogEx(NORMAL, "\n* Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); if (!AFL || !AFL->len) { - PrintAndLog("WARNING: AFL not found."); + PrintAndLogEx(WARNING, "AFL not found."); } while(AFL && AFL->len) { if (AFL->len % 4) { - PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len); + PrintAndLogEx(WARNING, "Error: Wrong AFL length: %d", AFL->len); break; } @@ -807,24 +889,24 @@ int CmdHFEMVExec(const char *cmd) { uint8_t SFIend = AFL->value[i * 4 + 2]; uint8_t SFIoffline = AFL->value[i * 4 + 3]; - PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { - PrintAndLog("SFI ERROR! Skipped..."); + PrintAndLogEx(NORMAL, "SFI ERROR! Skipped..."); continue; } for(int n = SFIstart; n <= SFIend; n++) { - PrintAndLog("* * * SFI[%02x] %d", SFI, n); + PrintAndLogEx(NORMAL, "* * * SFI[%02x] %d", SFI, n); - res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw); + PrintAndLogEx(WARNING, "Error SFI[%02x]. APDU error %4x", SFI, sw); continue; } if (decodeTLV) { TLVPrintFromBuffer(buf, len); - PrintAndLog(""); + PrintAndLogEx(NORMAL, ""); } // Build Input list for Offline Data Authentication @@ -838,7 +920,7 @@ int CmdHFEMVExec(const char *cmd) { memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen); ODAiListLen += elmlen; } else { - PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); + PrintAndLogEx(WARNING, "Error SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); } } else { memcpy(&ODAiList[ODAiListLen], buf, len); @@ -849,31 +931,36 @@ int CmdHFEMVExec(const char *cmd) { } break; - } + } // copy Input list for Offline Data Authentication if (ODAiListLen) { struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag tlvdb_add(tlvRoot, oda); - PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); + PrintAndLogEx(NORMAL, "* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); } // get AIP - const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); - uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; - PrintAndLog("* * AIP=%04x", AIP); + uint16_t AIP = 0; + const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); + if (AIPtlv) { + AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; + PrintAndLogEx(NORMAL, "* * AIP=%04x", AIP); + } else { + PrintAndLogEx(ERR, "Can't found AIP."); + } // SDA if (AIP & 0x0040) { - PrintAndLog("\n* SDA"); + PrintAndLogEx(NORMAL, "\n* SDA"); trSDA(tlvRoot); } // DDA if (AIP & 0x0020) { - PrintAndLog("\n* DDA"); - trDDA(decodeTLV, tlvRoot); - } + PrintAndLogEx(NORMAL, "\n* DDA"); + trDDA(channel, decodeTLV, tlvRoot); + } // transaction check @@ -882,8 +969,8 @@ int CmdHFEMVExec(const char *cmd) { // 9F26: Application Cryptogram const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); if (AC) { - PrintAndLog("\n--> qVSDC transaction."); - PrintAndLog("* AC path"); + PrintAndLogEx(NORMAL, "\n--> qVSDC transaction."); + PrintAndLogEx(NORMAL, "* AC path"); // 9F36: Application Transaction Counter (ATC) const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); @@ -893,24 +980,24 @@ int CmdHFEMVExec(const char *cmd) { const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); // print AC data - PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len)); - PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len)); + PrintAndLogEx(NORMAL, "ATC: %s", sprint_hex(ATC->value, ATC->len)); + PrintAndLogEx(NORMAL, "AC: %s", sprint_hex(AC->value, AC->len)); if (IAD){ - PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len)); + PrintAndLogEx(NORMAL, "IAD: %s", sprint_hex(IAD->value, IAD->len)); if (IAD->len >= IAD->value[0] + 1) { - PrintAndLog("\tKey index: 0x%02x", IAD->value[1]); - PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); - PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + PrintAndLogEx(NORMAL, "\tKey index: 0x%02x", IAD->value[1]); + PrintAndLogEx(NORMAL, "\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLogEx(NORMAL, "\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); TLVPrintFromTLVLev(cvr, 1); } } else { - PrintAndLog("WARNING: IAD not found."); + PrintAndLogEx(WARNING, "IAD not found."); } } else { - PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found."); + PrintAndLogEx(ERROR, "AC: Application Transaction Counter (ATC) not found."); } } } @@ -919,16 +1006,16 @@ int CmdHFEMVExec(const char *cmd) { if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){ const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag - PrintAndLog("\n--> Mastercard M/Chip transaction."); + PrintAndLogEx(NORMAL, "\n--> Mastercard M/Chip transaction."); - PrintAndLog("* * Generate challenge"); - res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); + PrintAndLogEx(NORMAL, "* * Generate challenge"); + res = EMVGenerateChallenge(channel, true, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); + PrintAndLogEx(WARNING, "GetChallenge. APDU error %4x", sw); dreturn(6); } if (len < 4) { - PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); + PrintAndLogEx(WARNING, "GetChallenge. Wrong challenge length %d", len); dreturn(6); } @@ -936,24 +1023,24 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); tlvdb_add(tlvRoot, ICCDynN); if (decodeTLV){ - PrintAndLog("\n* * ICC Dynamic Number:"); + PrintAndLogEx(NORMAL, "\n* * ICC Dynamic Number:"); TLVPrintFromTLV(ICCDynN); } - PrintAndLog("* * Calc CDOL1"); + PrintAndLogEx(NORMAL, "* * Calc CDOL1"); struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol_data_tlv){ - PrintAndLog("ERROR: can't create CDOL1 TLV."); + PrintAndLogEx(WARNING, "Error: can't create CDOL1 TLV."); dreturn(6); } - PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + PrintAndLogEx(NORMAL, "CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); - PrintAndLog("* * AC1"); + PrintAndLogEx(NORMAL, "* * AC1"); // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD - res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVAC(channel, true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); - if (res) { - PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); + if (res) { + PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw); dreturn(7); } @@ -961,41 +1048,41 @@ int CmdHFEMVExec(const char *cmd) { TLVPrintFromBuffer(buf, len); // CDA - PrintAndLog("\n* CDA:"); + PrintAndLogEx(NORMAL, "\n* CDA:"); struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); - if (res) { - PrintAndLog("CDA error (%d)", res); + if (res) { + PrintAndLogEx(NORMAL, "CDA error (%d)", res); } free(ac_tlv); free(cdol_data_tlv); - PrintAndLog("\n* M/Chip transaction result:"); + PrintAndLogEx(NORMAL, "\n* M/Chip transaction result:"); // 9F27: Cryptogram Information Data (CID) const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); if (CID) { emv_tag_dump(CID, stdout, 0); - PrintAndLog("------------------------------"); + PrintAndLogEx(NORMAL, "------------------------------"); if (CID->len > 0) { switch(CID->value[0] & EMVAC_AC_MASK){ case EMVAC_AAC: - PrintAndLog("Transaction DECLINED."); + PrintAndLogEx(NORMAL, "Transaction DECLINED."); break; case EMVAC_TC: - PrintAndLog("Transaction approved OFFLINE."); + PrintAndLogEx(NORMAL, "Transaction approved OFFLINE."); break; case EMVAC_ARQC: - PrintAndLog("Transaction approved ONLINE."); + PrintAndLogEx(NORMAL, "Transaction approved ONLINE."); break; default: - PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); + PrintAndLogEx(WARNING, "Error: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); break; } } else { - PrintAndLog("ERROR: Wrong CID length %d", CID->len); + PrintAndLogEx(WARNING, "Wrong CID length %d", CID->len); } } else { - PrintAndLog("ERROR: CID(9F27) not found."); + PrintAndLogEx(WARNING, "CID(9F27) not found."); } } @@ -1003,20 +1090,20 @@ int CmdHFEMVExec(const char *cmd) { // MSD if (AIP & 0x8000 && TrType == TT_MSD) { - PrintAndLog("\n--> MSD transaction."); + PrintAndLogEx(NORMAL, "\n--> MSD transaction."); - PrintAndLog("* MSD dCVV path. Check dCVV"); + PrintAndLogEx(NORMAL, "* MSD dCVV path. Check dCVV"); const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); if (track2) { - PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len)); + PrintAndLogEx(NORMAL, "Track2: %s", sprint_hex(track2->value, track2->len)); struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); - PrintAndLog("dCVV raw data:"); + PrintAndLogEx(NORMAL, "dCVV raw data:"); TLVPrintFromTLV(dCVV); if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { - PrintAndLog("\n* Mastercard calculate UDOL"); + PrintAndLogEx(NORMAL, "\n* Mastercard calculate UDOL"); // UDOL (9F69) const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); @@ -1027,38 +1114,37 @@ int CmdHFEMVExec(const char *cmd) { .value = (uint8_t *)"\x9f\x6a\x04", }; if (!UDOL) - PrintAndLog("Use default UDOL."); + PrintAndLogEx(NORMAL, "Use default UDOL."); struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv){ - PrintAndLog("ERROR: can't create UDOL TLV."); + PrintAndLogEx(WARNING, "can't create UDOL TLV."); dreturn(8); } - PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); + PrintAndLogEx(NORMAL, "UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); - PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); + PrintAndLogEx(NORMAL, "\n* Mastercard compute cryptographic checksum(UDOL)"); - res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + res = MSCComputeCryptoChecksum(channel, true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); + PrintAndLogEx(WARNING, "Compute Crypto Checksum. APDU error %4x", sw); free(udol_data_tlv); dreturn(9); } - if (decodeTLV) { - TLVPrintFromBuffer(buf, len); - PrintAndLog(""); - } + // Mastercard compute cryptographic checksum result + TLVPrintFromBuffer(buf, len); + PrintAndLogEx(NORMAL, ""); + free(udol_data_tlv); } } else { - PrintAndLog("ERROR MSD: Track2 data not found."); + PrintAndLogEx(WARNING, "MSD: Track2 data not found."); } } - // DropField DropField(); // Destroy TLV's @@ -1066,12 +1152,11 @@ int CmdHFEMVExec(const char *cmd) { tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); - PrintAndLog("\n* Transaction completed."); - + PrintAndLogEx(NORMAL, "\n* Transaction completed."); return 0; } -int CmdHFEMVScan(const char *cmd) { +int CmdEMVScan(const char *cmd) { uint8_t AID[APDU_AID_LEN] = {0}; size_t AIDlen = 0; uint8_t buf[APDU_RES_LEN] = {0}; @@ -1081,11 +1166,11 @@ int CmdHFEMVScan(const char *cmd) { json_t *root; json_error_t error; - CLIParserInit("hf emv scan", + CLIParserInit("emv scan", "Scan EMV card and save it contents to a file.", "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n" - "Usage:\n\thf emv scan -at -> scan MSD transaction mode and show APDU and TLV\n" - "\thf emv scan -c -> scan CDA transaction mode\n"); + "Usage:\n\temv scan -at -> scan MSD transaction mode and show APDU and TLV\n" + "\temv scan -c -> scan CDA transaction mode\n"); void* argtable[] = { arg_param_begin, @@ -1099,7 +1184,10 @@ int CmdHFEMVScan(const char *cmd) { arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), - arg_str1(NULL, NULL, "output.json", "JSON output file name"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif + arg_str1(NULL, NULL, "output.json", "JSON output file name"), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -1119,14 +1207,27 @@ int CmdHFEMVScan(const char *cmd) { bool GenACGPO = arg_get_lit(9); bool MergeJSON = arg_get_lit(10); - uint8_t relfname[250] ={0}; + EMVCommandChannel channel = ECC_CONTACTLESS; + uint8_t relfname[250] = {0}; char *crelfname = (char *)relfname; int relfnamelen = 0; +#ifdef WITH_SMARTCARD + if (arg_get_lit(11)) + channel = ECC_CONTACT; + CLIGetStrWithReturn(12, relfname, &relfnamelen); +#else CLIGetStrWithReturn(11, relfname, &relfnamelen); +#endif CLIParserFree(); SetAPDULogging(showAPDU); + // TODO + if (channel == ECC_CONTACT) { + PrintAndLogEx(ERR, "Do not use contact interface. Exit."); + return 1; + } + // current path + file name if (!strstr(crelfname, ".json")) strcat(crelfname, ".json"); @@ -1137,12 +1238,12 @@ int CmdHFEMVScan(const char *cmd) { if (MergeJSON) { root = json_load_file(fname, 0, &error); if (!root) { - PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + PrintAndLogEx(ERROR, "json error on line %d: %s", error.line, error.text); return 1; } if (!json_is_object(root)) { - PrintAndLog("ERROR: Invalid json format. root must be an object."); + PrintAndLogEx(ERROR, "Invalid json format. root must be an object."); return 1; } } else { @@ -1153,14 +1254,14 @@ int CmdHFEMVScan(const char *cmd) { DropField(); // iso 14443 select - PrintAndLog("--> GET UID, ATS."); + PrintAndLogEx(NORMAL, "--> GET UID, ATS."); iso14a_card_select_t card; if (Hf14443_4aGetCardData(&card)) { return 2; } - JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`"); + JsonSaveStr(root, "$.File.Created", "proxmark3 `emv scan`"); JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); @@ -1173,8 +1274,8 @@ int CmdHFEMVScan(const char *cmd) { struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); // EMV PPSE - PrintAndLog("--> PPSE."); - res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); + PrintAndLogEx(NORMAL, "--> PPSE."); + res = EMVSelectPSE(channel, true, true, 2, buf, sizeof(buf), &len, &sw); if (!res && sw == 0x9000){ if (decodeTLV) @@ -1191,17 +1292,17 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(fci); } - res = EMVSearchPSE(false, true, decodeTLV, tlvSelect); + res = EMVSearchPSE(channel, false, true, decodeTLV, tlvSelect); // check PPSE and select application id - if (!res) { - TLVPrintAIDlistFromSelectTLV(tlvSelect); + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); } else { // EMV SEARCH with AID list SetAPDULogging(false); - PrintAndLog("--> AID search."); - if (EMVSearch(false, true, decodeTLV, tlvSelect)) { - PrintAndLog("E->Can't found any of EMV AID. Exit..."); + PrintAndLogEx(NORMAL, "--> AID search."); + if (EMVSearch(channel, false, true, decodeTLV, tlvSelect)) { + PrintAndLogEx(ERROR, "Can't found any of EMV AID. Exit..."); tlvdb_free(tlvSelect); DropField(); return 3; @@ -1218,7 +1319,7 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(tlvSelect); if (!AIDlen) { - PrintAndLog("Can't select AID. EMV AID not found. Exit..."); + PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); DropField(); return 4; } @@ -1231,12 +1332,12 @@ int CmdHFEMVScan(const char *cmd) { // EMV SELECT applet - PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); + PrintAndLogEx(NORMAL, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); SetAPDULogging(showAPDU); - res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); - if (res) { - PrintAndLog("E->Can't select AID (%d). Exit...", res); + if (res) { + PrintAndLogEx(ERROR, "Can't select AID (%d). Exit...", res); tlvdb_free(tlvRoot); DropField(); return 5; @@ -1258,13 +1359,13 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(fci); // create transaction parameters - PrintAndLog("-->Init transaction parameters."); + PrintAndLogEx(NORMAL, "-->Init transaction parameters."); InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); - PrintAndLog("-->Calc PDOL."); + PrintAndLogEx(NORMAL, "-->Calc PDOL."); struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("E->Can't create PDOL TLV."); + PrintAndLogEx(ERROR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); DropField(); return 6; @@ -1273,21 +1374,21 @@ int CmdHFEMVScan(const char *cmd) { size_t pdol_data_tlv_data_len; unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); if (!pdol_data_tlv_data) { - PrintAndLog("E->Can't create PDOL data."); + PrintAndLogEx(ERROR, "Can't create PDOL data."); tlvdb_free(tlvRoot); DropField(); return 6; } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); - PrintAndLog("-->GPO."); - res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + PrintAndLogEx(INFO, "-->GPO."); + res = EMVGPO(channel, true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); free(pdol_data_tlv); - if (res) { - PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + if (res) { + PrintAndLogEx(ERROR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); DropField(); return 7; @@ -1305,12 +1406,12 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(gpofci); - PrintAndLog("-->Read records from AFL."); + PrintAndLogEx(INFO, "-->Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); while(AFL && AFL->len) { if (AFL->len % 4) { - PrintAndLog("E->Wrong AFL length: %d", AFL->len); + PrintAndLogEx(ERROR, "Wrong AFL length: %d", AFL->len); break; } @@ -1322,7 +1423,7 @@ int CmdHFEMVScan(const char *cmd) { sfijson = json_path_get(root, "$.Application.Records"); } if (!json_is_array(sfijson)) { - PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array."); + PrintAndLogEx(ERROR, "Internal logic error. `$.Application.Records` is not an array."); break; } for (int i = 0; i < AFL->len / 4; i++) { @@ -1331,24 +1432,24 @@ int CmdHFEMVScan(const char *cmd) { uint8_t SFIend = AFL->value[i * 4 + 2]; uint8_t SFIoffline = AFL->value[i * 4 + 3]; - PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + PrintAndLogEx(INFO, "--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { - PrintAndLog("SFI ERROR! Skipped..."); + PrintAndLogEx(ERROR, "SFI ERROR! Skipped..."); continue; } for(int n = SFIstart; n <= SFIend; n++) { - PrintAndLog("---->SFI[%02x] %d", SFI, n); + PrintAndLogEx(INFO, "---->SFI[%02x] %d", SFI, n); - res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw); + PrintAndLogEx(ERROR, "SFI[%02x]. APDU error %4x", SFI, sw); continue; } if (decodeTLV) { TLVPrintFromBuffer(buf, len); - PrintAndLog(""); + PrintAndLogEx(NORMAL, ""); } json_t *jsonelm = json_object(); @@ -1372,7 +1473,7 @@ int CmdHFEMVScan(const char *cmd) { // getting certificates if (tlvdb_get(tlvRoot, 0x90, NULL)) { - PrintAndLog("-->Recovering certificates."); + PrintAndLogEx(INFO, "-->Recovering certificates."); PKISetStrictExecution(false); RecoveryCertificates(tlvRoot, root); PKISetStrictExecution(true); @@ -1386,10 +1487,10 @@ int CmdHFEMVScan(const char *cmd) { res = json_dump_file(root, fname, JSON_INDENT(2)); if (res) { - PrintAndLog("ERROR: can't save the file: %s", fname); + PrintAndLogEx(ERROR, "Can't save the file: %s", fname); return 200; } - PrintAndLog("File `%s` saved.", fname); + PrintAndLogEx(SUCCESS, "File `%s` saved.", fname); // free json object json_decref(root); @@ -1397,28 +1498,28 @@ int CmdHFEMVScan(const char *cmd) { return 0; } -int CmdHFEMVTest(const char *cmd) { +int CmdEMVTest(const char *cmd) { return ExecuteCryptoTests(true); } int CmdHelp(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, - {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, - {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, - {"select", CmdHFEMVSelect, 0, "Select applet."}, - {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, - {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, - {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, - {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, - {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, - {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, - {"test", CmdHFEMVTest, 0, "Crypto logic test."}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, 1, "This help"}, + {"exec", CmdEMVExec, 0, "Executes EMV contactless transaction."}, + {"pse", CmdEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, + {"search", CmdEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, + {"select", CmdEMVSelect, 0, "Select applet."}, + {"gpo", CmdEMVGPO, 0, "Execute GetProcessingOptions."}, + {"readrec", CmdEMVReadRecord, 0, "Read files from card."}, + {"genac", CmdEMVAC, 0, "Generate ApplicationCryptogram."}, + {"challenge", CmdEMVGenerateChallenge, 0, "Generate challenge."}, + {"intauth", CmdEMVInternalAuthenticate, 0, "Internal authentication."}, + {"scan", CmdEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, + {"test", CmdEMVTest, 0, "Crypto logic test."}, + {NULL, NULL, 0, NULL} }; -int CmdHFEMV(const char *Cmd) { +int CmdEMV(const char *Cmd) { CmdsParse(CommandTable, Cmd); return 0; } diff --git a/client/emv/cmdemv.h b/client/emv/cmdemv.h index b3f76508..3dc76fa9 100644 --- a/client/emv/cmdemv.h +++ b/client/emv/cmdemv.h @@ -26,7 +26,13 @@ #include "emvcore.h" #include "apduinfo.h" -int CmdHFEMV(const char *Cmd); +int CmdEMV(const char *Cmd); +extern int CmdEMVSelect(const char *cmd); +extern int CmdEMVSearch(const char *cmd); +extern int CmdEMVPPSE(const char *cmd); +extern int CmdEMVExec(const char *cmd); +extern int CmdEMVGetrng(const char *Cmd); +extern int CmdEMVList(const char *Cmd); -#endif \ No newline at end of file +#endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index c1259114..04d8f44c 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -11,6 +11,9 @@ #include "emvcore.h" #include "emvjson.h" #include "util_posix.h" +#ifdef WITH_SMARTCARD +#include "cmdsmartcard.h" +#endif // Got from here. Thanks) // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix @@ -230,12 +233,13 @@ struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) { return tlvdb_fixed(0x02, dCVVlen, dCVV); } -int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { +int EMVExchangeEx(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { uint8_t data[APDU_RES_LEN] = {0}; *ResultLen = 0; if (sw) *sw = 0; uint16_t isw = 0; + int res = 0; if (ActivateField){ DropField(); @@ -250,16 +254,32 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ if (APDULogging) PrintAndLog(">>>> %s", sprint_hex(data, (IncludeLe?6:5) + apdu.Lc)); - // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le(?IncludeLe) - int res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); - - if (res) { - return res; + switch(channel) { + case ECC_CONTACTLESS: + // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le(?IncludeLe) + res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + if (res) { + return res; + } + break; + case ECC_CONTACT: + //int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); +#ifdef WITH_SMARTCARD + res = ExchangeAPDUSC(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + if (res) { + return res; + } +#endif + break; } if (APDULogging) PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + if (*ResultLen < 2) { + return 200; + } + *ResultLen -= 2; isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; if (sw) @@ -285,15 +305,15 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ return 0; } -int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchangeEx(false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(channel, false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv); } -int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(channel, ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv); } -int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { +int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { uint8_t buf[APDU_AID_LEN] = {0}; *ResultLen = 0; int len = 0; @@ -310,19 +330,19 @@ int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t } // select - res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); + res = EMVSelect(channel, ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); return res; } -int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { +int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { uint8_t data[APDU_RES_LEN] = {0}; size_t datalen = 0; uint16_t sw = 0; int res; // select PPSE - res = EMVSelectPSE(ActivateField, true, 2, data, sizeof(data), &datalen, &sw); + res = EMVSelectPSE(channel, ActivateField, true, 2, data, sizeof(data), &datalen, &sw); if (!res){ struct tlvdb *t = NULL; @@ -336,7 +356,7 @@ int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct t while (ttmp) { const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL); if (tgAID) { - res = EMVSelect(false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv); + res = EMVSelect(channel, false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv); // retry if error and not returned sw error if (res && res != 5) { @@ -383,7 +403,7 @@ int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct t return res; } -int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { +int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { uint8_t aidbuf[APDU_AID_LEN] = {0}; int aidlen = 0; uint8_t data[APDU_RES_LEN] = {0}; @@ -394,15 +414,15 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd int retrycnt = 0; for(int i = 0; i < AIDlistLen; i ++) { param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen); - res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); + res = EMVSelect(channel, (i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); // retry if error and not returned sw error if (res && res != 5) { if (++retrycnt < 3){ i--; } else { - // card select error, proxmark error - if (res == 1) { - PrintAndLog("Exit..."); + // (1) - card select error, proxmark error OR (200) - result length = 0 + if (res == 1 || res == 200) { + PrintAndLogEx(WARNING, "Exit..."); return 1; } @@ -464,38 +484,38 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { return 0; } -int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } -int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + int res = EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); if (*sw == 0x6700) { - PrintAndLog(">>> trying to reissue command withouth Le..."); - res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le..."); + res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); } return res; } -int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } -int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + int res = EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); if (*sw == 0x6700) { - PrintAndLog(">>> trying to reissue command withouth Le..."); - res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le..."); + res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); } return res; } -int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } -int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } // Authentication @@ -565,7 +585,7 @@ int trSDA(struct tlvdb *tlv) { static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04}; static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value }; -int trDDA(bool decodeTLV, struct tlvdb *tlv) { +int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -705,9 +725,9 @@ int trDDA(bool decodeTLV, struct tlvdb *tlv) { PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); PrintAndLog("\n* Internal Authenticate"); - int res = EMVInternalAuthenticate(true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL); + int res = EMVInternalAuthenticate(channel, true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL); if (res) { - PrintAndLog("Internal Authenticate error(%d): %4x. Exit...", res, sw); + PrintAndLogEx(WARNING, "Internal Authenticate error(%d): %4x. Exit...", res, sw); free(ddol_data_tlv); emv_pk_free(pk); emv_pk_free(issuer_pk); diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index fa7a4db8..d8b6a5c7 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -32,6 +32,11 @@ #define APDU_RES_LEN 260 #define APDU_AID_LEN 50 +typedef enum { + ECC_CONTACTLESS, + ECC_CONTACT +} EMVCommandChannel; + enum TransactionType { TT_MSD, TT_VSDC, // not standart for contactless!!!! @@ -71,29 +76,29 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2); extern void SetAPDULogging(bool logging); // exchange -extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // search application -extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); -extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); -extern int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -extern int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +extern int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +extern int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +extern int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // select application extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen); // Get Processing Options -extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); -extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // AC -extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); -extern int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // DDA -extern int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // Mastercard -int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // Auth extern int trSDA(struct tlvdb *tlv); -extern int trDDA(bool decodeTLV, struct tlvdb *tlv); +extern int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv); extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv); extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root); diff --git a/client/fido/fidocore.c b/client/fido/fidocore.c index 39c2052f..ee39fbbe 100644 --- a/client/fido/fidocore.c +++ b/client/fido/fidocore.c @@ -170,17 +170,17 @@ char *fido2GetCmdMemberDescription(uint8_t cmdCode, bool isResponse, int memberN int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01}; - return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL); + return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL); } int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL); + int res = EMVExchange(ECC_CONTACTLESS, true, apdu, Result, MaxResultLen, ResultLen, sw, NULL); if (res == 5) // apdu result (sw) not a 0x9000 res = 0; // software chaining while (!res && (*sw >> 8) == 0x61) { size_t oldlen = *ResultLen; - res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL); + res = EMVExchange(ECC_CONTACTLESS, true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL); if (res == 5) // apdu result (sw) not a 0x9000 res = 0; diff --git a/common/Makefile_Enabled_Options.common b/common/Makefile_Enabled_Options.common index 8774946e..d9f6d3b9 100644 --- a/common/Makefile_Enabled_Options.common +++ b/common/Makefile_Enabled_Options.common @@ -13,6 +13,7 @@ APP_CFLAGS += -DWITH_ISO14443a_StandAlone \ -DWITH_HITAG \ -DWITH_CRC \ -DWITH_HFSNOOP \ + -DWITH_SMARTCARD \ -DWITH_GUI #END diff --git a/common/protocols.h b/common/protocols.h index 06a80de1..79d8e083 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -262,6 +262,7 @@ NXP/Philips CUSTOM COMMANDS #define ISO7816_EXTERNAL_AUTHENTICATION 0x82 #define ISO7816_GET_CHALLENGE 0xB4 #define ISO7816_MANAGE_CHANNEL 0x70 +#define ISO7816_GETSTATUS 0xC0 // ISO7816-4 For response APDU's #define ISO7816_OK 0x9000 // 6x xx = ERROR diff --git a/include/smartcard.h b/include/smartcard.h index 9bed8c9d..2f6e286c 100644 --- a/include/smartcard.h +++ b/include/smartcard.h @@ -22,7 +22,7 @@ typedef enum SMARTCARD_COMMAND { SC_CONNECT = (1 << 0), SC_NO_DISCONNECT = (1 << 1), SC_RAW = (1 << 2), - SC_NO_SELECT = (1 << 3) + SC_SELECT = (1 << 3) } smartcard_command_t;