From: Iceman Date: Tue, 28 Nov 2017 20:26:57 +0000 (+0100) Subject: Merge pull request #488 from pwpiwi/fix_usbpower X-Git-Tag: v3.1.0~115 X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/653e5ed3ca3f8bf4ae677900ae4860a8cf7026c1?hp=f380317220a8326545c528f5a054238d54b9046c Merge pull request #488 from pwpiwi/fix_usbpower fix: USB Power requirements --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 10e6b827..fabd04b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) - Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) +- Changed TLV parser messages to more convenient (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -38,6 +39,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) - Added to `hf 14a apdu` - exchange apdu via iso1443-4 (Merlok) - Added to `hf 14a apdu` - apdu and tlv results parser (Merlok) +- Added `hf emv` group of commands (Merlok) +- Added `hf emv search` `hf emv pse` - commands for selection of EMV application (Merlok) +- Added `hf emv select` - command for select EMV application (Merlok) ## [3.0.1][2017-06-08] diff --git a/README.md b/README.md new file mode 100644 index 00000000..7258f918 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# proxmark3: the official Proxmark repository! + +The proxmark3 is a powerful general purpose RFID tool, the size of a deck +of cards, designed to snoop, listen and emulate everything from +**Low Frequency (125kHz)** to **High Frequency (13.56MHz)** tags. + +This repository contains enough software, logic (for the FPGA), and design +documentation for the hardware that you could, at least in theory, +do something useful with a proxmark3. + +## Resources + +* [This repository!](https://github.com/Proxmark/proxmark3) +* [The Wiki](https://github.com/Proxmark/proxmark3/wiki) +* [The GitHub Pages website](http://proxmark.github.io/proxmark3/) +* [The Forum](http://www.proxmark.org/forum) +* The IRC channel: irc.freenode.org #proxmark3 ([chat in your browser](http://webchat.freenode.net/?channels=#proxmark3)) +* [The Homebrew formula repository](https://github.com/Proxmark/homebrew-proxmark3) + +## Development + +The tools required to build or run the project will vary depending on +your operating system. Please refer to [the wiki](https://github.com/Proxmark/proxmark3/wiki) for details. + +## Obtaining hardware + +The Proxmark3 is available for purchase (assembled and tested) from the +following locations: + +* [RyscCorp](https://proxmark3.com/) (US) +* [Elechouse](http://www.elechouse.com/) (HK) +* [Lab401](https://lab401.com/) (FR) +* [RFxSecure](http://www.rfxsecure.com/) (SG) +* [IceSQL](http://proxmark3.tictail.com/) (SE) + +Most of the ultra-low-volume contract assemblers could put +something like this together with a reasonable yield. A run of around +a dozen units is probably cost-effective. The BOM includes (possibly- +outdated) component pricing, and everything is available from Digikey +and the usual distributors. + +If you've never assembled a modern circuit board by hand, then this is +not a good place to start. Some of the components (e.g. the crystals) +must not be assembled with a soldering iron, and require hot air. + +The schematics are included; the component values given are not +necessarily correct for all situations, but it should be possible to do +nearly anything you would want with appropriate population options. + +The printed circuit board artwork is also available, as Gerbers and an +Excellon drill file. + + +## License + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Jonathan Westhues +user jwesthues, at host cq.cx + +May 2007, Cambridge MA diff --git a/README.txt b/README.txt deleted file mode 100644 index c8cac497..00000000 --- a/README.txt +++ /dev/null @@ -1,93 +0,0 @@ -NOTICE: -(2014-03-26) -This is now the official Proxmark repository! - -INTRODUCTION: - -The proxmark3 is a powerful general purpose RFID tool, the size of a deck -of cards, designed to snoop, listen and emulate everything from -Low Frequency (125kHz) to High Frequency (13.56MHz) tags. - -This repository contains enough software, logic (for the FPGA), and design -documentation for the hardware that you could, at least in theory, -do something useful with a proxmark3. - -RESOURCES: - - * This repository! - https://github.com/Proxmark/proxmark3 - - * The Wiki - https://github.com/Proxmark/proxmark3/wiki - - * The GitHub page - http://proxmark.github.io/proxmark3/ - - * The Forum - http://www.proxmark.org/forum - - * The IRC chanel - irc.freenode.org #proxmark3 - -or- - http://webchat.freenode.net/?channels=#proxmark3 - - * The Homebrew formula repository - https://github.com/Proxmark/homebrew-proxmark3 - -DEVELOPMENT: - -The tools required to build or run the project will vary depending on -your operating system. Please refer to the Wiki for details. - - * https://github.com/Proxmark/proxmark3/wiki - -OBTAINING HARDWARE: - -The Proxmark3 is available for purchase (assembled and tested) from the -following locations: - - * https://proxmark3.com/ - RyscCorp (us) - * http://www.elechouse.com/ - Elechouse (HK) - * https://lab401.com/ - Lab401 (FR) - * http://www.rfxsecure.com/ - RFxSecure (SG) - * http://proxmark3.tictail.com/ - IceSQL (SE) - -Most of the ultra-low-volume contract assemblers could put -something like this together with a reasonable yield. A run of around -a dozen units is probably cost-effective. The BOM includes (possibly- -outdated) component pricing, and everything is available from Digikey -and the usual distributors. - -If you've never assembled a modern circuit board by hand, then this is -not a good place to start. Some of the components (e.g. the crystals) -must not be assembled with a soldering iron, and require hot air. - -The schematics are included; the component values given are not -necessarily correct for all situations, but it should be possible to do -nearly anything you would want with appropriate population options. - -The printed circuit board artwork is also available, as Gerbers and an -Excellon drill file. - - -LICENSING: - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Jonathan Westhues -user jwesthues, at host cq.cx - -May 2007, Cambridge MA diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 89ef23d4..a0fe2326 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -189,33 +189,15 @@ void iso14a_set_trigger(bool enable) { void iso14a_set_timeout(uint32_t timeout) { - iso14a_timeout = timeout; - if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); + iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8); + if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106); } -static void iso14a_set_ATS_timeout(uint8_t *ats) { - - uint8_t tb1; - uint8_t fwi; - uint32_t fwt; - - if (ats[0] > 1) { // there is a format byte T0 - if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) - if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) - tb1 = ats[3]; - } else { - tb1 = ats[2]; - } - fwi = (tb1 & 0xf0) >> 4; // frame waiting indicator (FWI) - fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc - - iso14a_set_timeout(fwt/(8*16)); - } - } +uint32_t iso14a_get_timeout(void) { + return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8); } - //----------------------------------------------------------------------------- // Generate the parity value for a byte sequence // @@ -1277,7 +1259,7 @@ static void PrepareDelayedTransfer(uint16_t delay) // Transmit the command (to the tag) that was placed in ToSend[]. // Parameter timing: // if NULL: transfer at next possible time, taking into account -// request guard time and frame delay time +// request guard time, startup frame guard time and frame delay time // if == 0: transfer immediately and return time of transfer // if != 0: delay transfer until time specified //------------------------------------------------------------------------------------- @@ -1685,6 +1667,59 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) return Demod.len; } + +static void iso14a_set_ATS_times(uint8_t *ats) { + + uint8_t tb1; + uint8_t fwi, sfgi; + uint32_t fwt, sfgt; + + if (ats[0] > 1) { // there is a format byte T0 + if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) + if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) + tb1 = ats[3]; + } else { + tb1 = ats[2]; + } + fwi = (tb1 & 0xf0) >> 4; // frame waiting time integer (FWI) + if (fwi != 15) { + fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc + iso14a_set_timeout(fwt/(8*16)); + } + sfgi = tb1 & 0x0f; // startup frame guard time integer (SFGI) + if (sfgi != 0 && sfgi != 15) { + sfgt = 256 * 16 * (1 << sfgi); // startup frame guard time (SFGT) in 1/fc + NextTransferTime = MAX(NextTransferTime, Demod.endTime + (sfgt - DELAY_AIR2ARM_AS_READER - DELAY_ARM2AIR_AS_READER)/16); + } + } + } +} + + +static int GetATQA(uint8_t *resp, uint8_t *resp_par) { + +#define WUPA_RETRY_TIMEOUT 10 // 10ms + uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP + + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(1236/(16*8)+1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + + uint32_t start_time = GetTickCount(); + int len; + + // we may need several tries if we did send an unknown command or a wrong authentication before... + do { + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field + ReaderTransmitBitsPar(wupa, 7, NULL, NULL); + // Receive the ATQA + len = ReaderReceive(resp, resp_par); + } while (len == 0 && GetTickCount() <= start_time + WUPA_RETRY_TIMEOUT); + + iso14a_set_timeout(save_iso14a_timeout); + return len; +} + + // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL // fills the card info record unless NULL @@ -1692,7 +1727,6 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) // and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) // requests ATS unless no_rats is true int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { - uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP uint8_t sel_all[] = { 0x93,0x20 }; uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 @@ -1712,11 +1746,9 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u p_hi14a_card->ats_len = 0; } - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitBitsPar(wupa, 7, NULL, NULL); - - // Receive the ATQA - if(!ReaderReceive(resp, resp_par)) return 0; + if (!GetATQA(resp, resp_par)) { + return 0; + } if(p_hi14a_card) { memcpy(p_hi14a_card->atqa, resp, 2); @@ -1845,8 +1877,9 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // reset the PCB block number iso14_pcb_blocknum = 0; - // set default timeout based on ATS - iso14a_set_ATS_timeout(resp); + // set default timeout and delay next transfer based on ATS + iso14a_set_ATS_times(resp); + } return 1; } diff --git a/armsrc/util.c b/armsrc/util.c index a0fa50d3..e25c6e0b 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -292,6 +292,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers strncat(dst, "\n", len - strlen(dst) - 1); } + // ------------------------------------------------------------------------- // timer lib // ------------------------------------------------------------------------- @@ -312,6 +313,7 @@ void StartTickCount() // note: worst case precision is approx 2.5% } + /* * Get the current count. */ @@ -319,6 +321,7 @@ uint32_t RAMFUNC GetTickCount(){ return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; } + // ------------------------------------------------------------------------- // microseconds timer // ------------------------------------------------------------------------- @@ -344,10 +347,12 @@ void StartCountUS() AT91C_BASE_TCB->TCB_BCR = 1; } + uint32_t RAMFUNC GetCountUS(){ return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV * 2) / 3); //was /15) * 10); } + static uint32_t GlobalUsCounter = 0; uint32_t RAMFUNC GetDeltaCountUS(){ @@ -402,7 +407,7 @@ void StartCountSspClk() AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN; // enable TC2 // - // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14446 mode, otherwise the frame signal would not be present + // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14443 mode, otherwise SSC_FRAME and SSC_CLK signals would not be present // while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low @@ -416,8 +421,11 @@ void StartCountSspClk() // (just started with the transfer of the 4th Bit). // The high word of the counter (TC2) will not reset until the low word (TC0) overflows. Therefore need to wait quite some time before // we can use the counter. - while (AT91C_BASE_TC0->TC_CV < 0xFFF0); + while (AT91C_BASE_TC0->TC_CV < 0xFFFF); + // Note: needs one more SSP_CLK cycle (1.18 us) until TC2 resets. Don't call GetCountSspClk() that soon. } + + void ResetSspClk(void) { //enable clock of timer and software trigger AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; @@ -425,6 +433,8 @@ void ResetSspClk(void) { AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while (AT91C_BASE_TC2->TC_CV > 0); } + + uint32_t RAMFUNC GetCountSspClk(){ uint32_t tmp_count; tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; @@ -436,6 +446,7 @@ uint32_t RAMFUNC GetCountSspClk(){ } } + // ------------------------------------------------------------------------- // Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks @@ -464,6 +475,7 @@ void StartTicks(void){ while (AT91C_BASE_TC1->TC_CV > 0); } + // Wait - Spindelay in ticks. // if called with a high number, this will trigger the WDT... void WaitTicks(uint32_t ticks){ @@ -471,32 +483,43 @@ void WaitTicks(uint32_t ticks){ ticks += GET_TICKS; while (GET_TICKS < ticks); } + + // Wait / Spindelay in us (microseconds) // 1us = 1.5ticks. void WaitUS(uint16_t us){ if ( us == 0 ) return; WaitTicks( (uint32_t)(us * 1.5) ); } + + void WaitMS(uint16_t ms){ if (ms == 0) return; WaitTicks( (uint32_t)(ms * 1500) ); } + + // Starts Clock and waits until its reset void ResetTicks(void){ AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while (AT91C_BASE_TC1->TC_CV > 0); } + + void ResetTimer(AT91PS_TC timer){ timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while(timer->TC_CV > 0) ; } + + // stop clock void StopTicks(void){ AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; } + static uint64_t next_random = 1; /* Generates a (non-cryptographically secure) 32-bit random number. @@ -512,4 +535,3 @@ uint32_t prand() { next_random = next_random * 6364136223846793005 + 1; return (uint32_t)(next_random >> 32) % 0xffffffff; } - diff --git a/client/Makefile b/client/Makefile index 30e91efb..5df99de7 100644 --- a/client/Makefile +++ b/client/Makefile @@ -111,7 +111,9 @@ CMDSRCS = crapto1/crapto1.c\ emv/dump.c\ emv/tlv.c\ emv/emv_tags.c\ + emv/dol.c\ emv/emvcore.c\ + emv/cmdemv.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf.c b/client/cmdhf.c index 453635b7..7a2f3252 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -30,6 +30,7 @@ #include "cmdhfmfu.h" #include "cmdhftopaz.h" #include "protocols.h" +#include "emv/cmdemv.h" static int CmdHelp(const char *Cmd); @@ -703,6 +704,7 @@ 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/cmdhf14a.c b/client/cmdhf14a.c index 50071464..a0f3ce57 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -648,7 +648,7 @@ void DropField() { SendCommand(&c); } -int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen) { +int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { uint16_t cmdc = 0; if (activateField) { @@ -686,6 +686,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea *dataoutlen = iLen - 2; if (*dataoutlen < 0) *dataoutlen = 0; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + memcpy(dataout, recv, *dataoutlen); if(!iLen) { @@ -779,7 +785,7 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); - int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen); + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen); if (res) return res; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 6f6df907..401cead0 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,6 @@ int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); extern void DropField(); -extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int *dataoutlen); +extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); #endif diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c new file mode 100644 index 00000000..3c3c1f11 --- /dev/null +++ b/client/emv/cmdemv.c @@ -0,0 +1,552 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV commands +//----------------------------------------------------------------------------- + +#include "cmdemv.h" + +int UsageCmdHFEMVSelect(void) { + PrintAndLog("HELP : Executes select applet command:\n"); + PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] \n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -k : keep field for next command"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet"); + PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); + return 0; +} + +int CmdHFEMVSelect(const char *cmd) { + uint8_t data[APDU_AID_LEN] = {0}; + int datalen = 0; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVSelect(); + return 0; + } + + SetAPDULogging(false); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVSelect(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 'a': + case 'A': + SetAPDULogging(true); + break; + case 't': + case 'T': + decodeTLV = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + + if (isxdigit(c)) { + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("AID too large."); + return 1; + case 3: + PrintAndLog("Hex must have even number of digits."); + return 1; + } + + // we get all the hex to end of line with spaces + break; + } + + + cmdp++; + } + + // exec + 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); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int UsageCmdHFEMVSearch(void) { + PrintAndLog("HELP : Tries to select all applets from applet list:\n"); + PrintAndLog("Usage: hf emv search [-s][-k][-a][-t]\n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -k : keep field for next command"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results of selected applets\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv search -s -> select card and search"); + PrintAndLog(" hf emv search -s -t -> select card, search and show result in TLV"); + return 0; +} + +int CmdHFEMVSearch(const char *cmd) { + + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVSearch(); + return 0; + } + + SetAPDULogging(false); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVSearch(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 'a': + case 'A': + SetAPDULogging(true); + break; + case 't': + case 'T': + decodeTLV = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + cmdp++; + } + + struct tlvdb *t = NULL; + const char *al = "Applets list"; + t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) { + tlvdb_free(t); + return 2; + } + + PrintAndLog("Search completed."); + + // print list here + if (!decodeTLV) { + TLVPrintAIDlistFromSelectTLV(t); + } + + tlvdb_free(t); + + return 0; +} + +int UsageCmdHFEMVPPSE(void) { + PrintAndLog("HELP : Executes PSE/PPSE select command. It returns list of applet on the card:\n"); + PrintAndLog("Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -k : keep field for next command"); + PrintAndLog(" -1 : ppse (1PAY.SYS.DDF01)"); + PrintAndLog(" -2 : pse (2PAY.SYS.DDF01)"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv pse -s -1 -> select, get pse"); + PrintAndLog(" hf emv pse -s -k -2 -> select, get ppse, keep field"); + PrintAndLog(" hf emv pse -s -t -2 -> select, get ppse, show result in TLV"); + return 0; +} + +int CmdHFEMVPPSE(const char *cmd) { + + uint8_t PSENum = 2; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVPPSE(); + return 0; + } + + SetAPDULogging(false); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVPPSE(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'k': + case 'K': + leaveSignalON = true; + break; + case 'a': + case 'A': + SetAPDULogging(true); + break; + case 't': + case 'T': + decodeTLV = true; + break; + case '1': + PSENum = 1; + break; + case '2': + PSENum = 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + cmdp++; + } + + // exec + 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); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int UsageCmdHFEMVExec(void) { + PrintAndLog("HELP : Executes EMV contactless transaction:\n"); + PrintAndLog("Usage: hf emv exec [-s][-a][-t]\n"); + PrintAndLog(" Options:"); + PrintAndLog(" -s : select card"); + PrintAndLog(" -a : show APDU reqests and responses\n"); + PrintAndLog(" -t : TLV decode results\n"); + PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); + PrintAndLog("Samples:"); + PrintAndLog(" hf emv pse -s -> select card"); + PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU"); + return 0; +} + +#define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) + +int CmdHFEMVExec(const char *cmd) { + bool activateField = false; + bool showAPDU = false; + bool decodeTLV = false; + bool forceSearch = false; + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + + int res; + + if (strlen(cmd) < 1) { + UsageCmdHFEMVExec(); + return 0; + } + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (param_getchar_indx(cmd, 1, cmdp)) { + case 'h': + case 'H': + UsageCmdHFEMVPPSE(); + return 0; + case 's': + case 'S': + activateField = true; + break; + case 'a': + case 'A': + showAPDU = true; + break; + case 't': + case 'T': + decodeTLV = true; + break; + case 'f': + case 'F': + forceSearch = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + cmdp++; + } + + + // init applets list tree + struct tlvdb *tlvSelect = NULL; + const char *al = "Applets list"; + tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + // Application Selection + // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html + if (!forceSearch) { + // PPSE + PrintAndLog("\n* PPSE."); + SetAPDULogging(showAPDU); + res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); + + // check PPSE and select application id + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + } + } + + // Search + if (!AIDlen) { + PrintAndLog("\n* Search AID in list."); + SetAPDULogging(false); + if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { + tlvdb_free(tlvSelect); + return 2; + } + + // check search and select application id + TLVPrintAIDlistFromSelectTLV(tlvSelect); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + } + + // Init TLV tree + struct tlvdb *tlvRoot = NULL; + const char *alr = "Root terminal TLV tree"; + tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // check if we found EMV application on card + if (!AIDlen) { + PrintAndLog("Can't select AID. EMV AID not found"); + return 2; + } + + // Select + PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(showAPDU); + res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("Can't select AID (%d). Exit...", res); + return 3; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + PrintAndLog("* Selected."); + +PrintAndLog("-----BREAK."); +return 0; + PrintAndLog("\n* Init transaction parameters."); + + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6 + //9F02:(Amount, Authorised (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + + TLVPrintFromTLV(tlvRoot); + + PrintAndLog("\n* Calc PDOL."); + struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + return 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."); + return 4; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + +//PrintAndLog("-----BREAK."); +//return 0; + PrintAndLog("\n* GPO."); + res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv); + + if (res) { + PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + return 5; + } + + // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] + if (buf[0] == 0x80) { + + + + if (decodeTLV){ + PrintAndLog("GPO response format1:"); + TLVPrintFromBuffer(buf, len); + } + } else { + + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + } + + PrintAndLog("\n* Read records from AFL."); + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + if (!AFL || !AFL->len) { + PrintAndLog("WARNING: AFL not found."); + } + + while(AFL && AFL->len) { + if (AFL->len % 4) { + PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len); + break; + } + + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + uint8_t SFIoffline = AFL->value[i * 4 + 3]; + + PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + PrintAndLog("SFI ERROR! Skipped..."); + continue; + } + + for(int n = SFIstart; n <= SFIend; n++) { + PrintAndLog("* * * SFI[%02x] %d", SFI, n); + + res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw); + continue; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + if (SFIoffline) { + // here will be offline records storing... + // dont foget: if (sfi < 11) + } + } + } + + break; + } + + // additional contacless EMV commands (fDDA, CDA, external authenticate) + + + // DropField + DropField(); + + // Destroy TLV's + tlvdb_free(tlvSelect); + tlvdb_free(tlvRoot); + + PrintAndLog("\n* Transaction completed."); + + return 0; +} + +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."}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFEMV(const char *Cmd) { + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/emv/cmdemv.h b/client/emv/cmdemv.h new file mode 100644 index 00000000..78796efa --- /dev/null +++ b/client/emv/cmdemv.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV commands +//----------------------------------------------------------------------------- + +#ifndef CMDEMV_H__ +#define CMDEMV_H__ + +#include +#include +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "util_posix.h" +#include "cmdmain.h" +#include "emvcore.h" +#include "apduinfo.h" + +int CmdHFEMV(const char *Cmd); + + +#endif \ No newline at end of file diff --git a/client/emv/dol.c b/client/emv/dol.c new file mode 100644 index 00000000..0fc1c49b --- /dev/null +++ b/client/emv/dol.c @@ -0,0 +1,135 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv/dol.h" +#include "emv/tlv.h" + +#include +#include + +static size_t dol_calculate_len(const struct tlv *tlv, size_t data_len) +{ + if (!tlv) + return 0; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t count = 0; + + while (left) { + struct tlv tlv; + if (!tlv_parse_tl(&buf, &left, &tlv)) + return 0; + + count += tlv.len; + + /* Last tag can be of variable length */ + if (tlv.len == 0 && left == 0) + count = data_len; + } + + return count; +} + +struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag) +{ + size_t res_len; + if (!tlv || !(res_len = dol_calculate_len(tlv, 0))) { + struct tlv *res_tlv = malloc(sizeof(*res_tlv)); + + res_tlv->tag = tag; + res_tlv->len = 0; + res_tlv->value = NULL; + + return res_tlv; + } + + struct tlv *res_tlv = malloc(sizeof(*res_tlv) + res_len); + if (!res_tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + unsigned char *res = (unsigned char *)(res_tlv + 1); + size_t pos = 0; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + free(res_tlv); + + return NULL; + } + + const struct tlv *tag_tlv = tlvdb_get(tlvdb, cur_tlv.tag, NULL); + if (!tag_tlv) { + memset(res + pos, 0, cur_tlv.len); + } else if (tag_tlv->len > cur_tlv.len) { + memcpy(res + pos, tag_tlv->value, cur_tlv.len); + } else { + // FIXME: cn data should be padded with 0xFF !!! + memcpy(res + pos, tag_tlv->value, tag_tlv->len); + memset(res + pos + tag_tlv->len, 0, cur_tlv.len - tag_tlv->len); + } + pos += cur_tlv.len; + } + + res_tlv->tag = tag; + res_tlv->len = res_len; + res_tlv->value = res; + + return res_tlv; +} + +struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *data, size_t data_len) +{ + if (!tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t res_len = dol_calculate_len(tlv, data_len); + size_t pos = 0; + struct tlvdb *db = NULL; + + if (res_len != data_len) + return NULL; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + tlvdb_free(db); + return NULL; + } + + /* Last tag can be of variable length */ + if (cur_tlv.len == 0 && left == 0) + cur_tlv.len = res_len - pos; + + struct tlvdb *tag_db = tlvdb_fixed(cur_tlv.tag, cur_tlv.len, data + pos); + if (!db) + db = tag_db; + else + tlvdb_add(db, tag_db); + + pos += cur_tlv.len; + } + + return db; +} diff --git a/client/emv/dol.h b/client/emv/dol.h new file mode 100644 index 00000000..9b1b5165 --- /dev/null +++ b/client/emv/dol.h @@ -0,0 +1,25 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DOL_H +#define DOL_H + +#include "emv/tlv.h" +#include + +struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *buf, size_t len); + +#endif diff --git a/client/emv/dump.c b/client/emv/dump.c index 41d7c9fd..2368680a 100644 --- a/client/emv/dump.c +++ b/client/emv/dump.c @@ -18,8 +18,8 @@ #endif #include "dump.h" - #include +#include #define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} diff --git a/client/emv/dump.h b/client/emv/dump.h index 567a134f..a981615a 100644 --- a/client/emv/dump.h +++ b/client/emv/dump.h @@ -21,5 +21,6 @@ void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level); +void dump_buffer_tab(const unsigned char *ptr, size_t len, FILE *f, int tabs); #endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index d91685c4..a7c11c3e 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -29,6 +29,7 @@ enum emv_tag_t { EMV_TAG_BITMASK, EMV_TAG_DOL, EMV_TAG_CVM_LIST, + EMV_TAG_AFL, EMV_TAG_STRING, EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, @@ -57,7 +58,7 @@ static const struct emv_tag_bit EMV_AIP[] = { { EMV_BIT(1, 3), "Issuer authentication is supported" }, { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(1, 1), "CDA supported" }, - { EMV_BIT(2, 8), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 8), "MSD is supported (Magnetic Stripe Data)" }, { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, @@ -113,8 +114,44 @@ static const struct emv_tag_bit EMV_TVR[] = { EMV_BIT_FINISH, }; +static const struct emv_tag_bit EMV_CTQ[] = { + { EMV_BIT(1, 8), "Online PIN Required" }, + { EMV_BIT(1, 7), "Signature Required" }, + { EMV_BIT(1, 6), "Go Online if Offline Data Authentication Fails and Reader is online capable" }, + { EMV_BIT(1, 5), "Switch Interface if Offline Data Authentication fails and Reader supports VIS" }, + { EMV_BIT(1, 4), "Go Online if Application Expired" }, + { EMV_BIT(1, 3), "Switch Interface for Cash Transactions" }, + { EMV_BIT(1, 2), "Switch Interface for Cashback Transactions" }, + { EMV_BIT(2, 8), "Consumer Device CVM Performed" }, + { EMV_BIT(2, 7), "Card supports Issuer Update Processing at the POS" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TTQ[] = { + { EMV_BIT(1, 8), "MSD supported" }, + { EMV_BIT(1, 7), "VSDC supported" }, + { EMV_BIT(1, 6), "qVSDC supported" }, + { EMV_BIT(1, 5), "EMV contact chip supported" }, + { EMV_BIT(1, 4), "Offline-only reader" }, + { EMV_BIT(1, 3), "Online PIN supported" }, + { EMV_BIT(1, 2), "Signature supported" }, + { EMV_BIT(1, 1), "Offline Data Authentication (ODA) for Online Authorizations supported\nWarning!!!! Readers compliant to this specification set TTQ byte 1 bit 1 (this field) to 0b" }, + { EMV_BIT(2, 8), "Online cryptogram required" }, + { EMV_BIT(2, 7), "CVM required" }, + { EMV_BIT(2, 6), "(Contact Chip) Offline PIN supported" }, + { EMV_BIT(3, 8), "Issuer Update Processing supported" }, + { EMV_BIT(3, 7), "Mobile functionality supported (Consumer Device CVM)" }, + EMV_BIT_FINISH, +}; + +// All Data Elements by Tags used in TLV structure (according to the EMV 4.2 Standard ) +// https://www.eftlab.co.uk/index.php/site-map/knowledge-base/145-emv-nfc-tags +// http://dexterous-programmer.blogspot.in/2012/05/emv-tags.html static const struct emv_tag emv_tags[] = { { 0x00 , "Unknown ???" }, + { 0x01 , "", EMV_TAG_STRING }, // string for headers + { 0x41 , "Country code and national data" }, + { 0x42 , "Issuer Identification Number (IIN)" }, { 0x4f , "Application Dedicated File (ADF) Name" }, { 0x50 , "Application Label", EMV_TAG_STRING }, { 0x56 , "Track 1 Data" }, @@ -147,12 +184,13 @@ static const struct emv_tag emv_tags[] = { { 0x91 , "Issuer Authentication Data" }, { 0x92 , "Issuer Public Key Remainder" }, { 0x93 , "Signed Static Application Data" }, - { 0x94 , "Application File Locator (AFL)" }, + { 0x94 , "Application File Locator (AFL)", EMV_TAG_AFL }, { 0x95 , "Terminal Verification Results" }, { 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, { 0x9c , "Transaction Type" }, { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, { 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, }, + { 0x9f06, "Application Identifier (AID), Terminal. ISO 7816-5" }, { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, { 0x9f08, "Application Version Number" }, { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, @@ -168,6 +206,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f21, "Transaction Time" }, { 0x9f26, "Application Cryptogram" }, { 0x9f27, "Cryptogram Information Data" }, + { 0x9f2a, "Kernel Identifier" }, { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, @@ -193,9 +232,12 @@ static const struct emv_tag emv_tags[] = { { 0x9f63, "PUNATC(Track1)" }, { 0x9f64, "NATC(Track1)" }, { 0x9f65, "PCVC3(Track2)" }, - { 0x9f66, "PUNATC(Track2)" }, - { 0x9f67, "NATC(Track2)" }, + { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ }, + { 0x9f67, "NATC(Track2) / MSD Offset" }, + { 0x9f69, "Card Authentication Related Data" }, + { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC }, { 0x9f6b, "Track 2 Data" }, + { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, { 0xa5 , "File Control Information (FCI) Proprietary Template" }, { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, }; @@ -242,10 +284,11 @@ static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *ta PRINT_INDENT(level); fprintf(f, "\tByte %u (%02x)\n", byte, val); for (bit = 8; bit > 0; bit--, val <<= 1) { - if (val & 0x80) + if (val & 0x80){ PRINT_INDENT(level); fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); + } if (bits->bit == EMV_BIT(byte, bit)) bits ++; } @@ -276,10 +319,8 @@ static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, F static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { - PRINT_INDENT(level); fprintf(f, "\tString value '"); fwrite(tlv->value, 1, tlv->len, f); - PRINT_INDENT(level); fprintf(f, "'\n"); } @@ -433,6 +474,19 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t } } +static void emv_tag_dump_afl(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level){ + if (tlv->len < 4 || tlv->len % 4) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + for (int i = 0; i < tlv->len / 4; i++) { + PRINT_INDENT(level); + fprintf(f, "SFI[%02x] start:%02x end:%02x offline:%02x\n", tlv->value[i * 4 + 0] >> 3, tlv->value[i * 4 + 1], tlv->value[i * 4 + 2], tlv->value[i * 4 + 3]); + } +} + bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) { if (!tlv) { @@ -443,20 +497,28 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) const struct emv_tag *tag = emv_get_tag(tlv); PRINT_INDENT(level); - fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); + fprintf(f, "--%2hx[%02zx] '%s':", tlv->tag, tlv->len, tag->name); switch (tag->type) { case EMV_TAG_GENERIC: + fprintf(f, "\n"); break; case EMV_TAG_BITMASK: + fprintf(f, "\n"); emv_tag_dump_bitmask(tlv, tag, f, level); break; case EMV_TAG_DOL: + fprintf(f, "\n"); emv_tag_dump_dol(tlv, tag, f, level); break; case EMV_TAG_CVM_LIST: + fprintf(f, "\n"); emv_tag_dump_cvm_list(tlv, tag, f, level); break; + case EMV_TAG_AFL: + fprintf(f, "\n"); + emv_tag_dump_afl(tlv, tag, f, level); + break; case EMV_TAG_STRING: emv_tag_dump_string(tlv, tag, f, level); break; diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 63a69baa..5c737b05 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -10,6 +10,77 @@ #include "emvcore.h" +// Got from here. Thanks) +// https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix +const char *PSElist [] = { + "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE + "315041592E5359532E4444463031" // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE +}; +const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); + +const char *AIDlist [] = { + // Visa International + "A00000000305076010", // VISA ELO Credit + "A0000000031010", // VISA Debit/Credit (Classic) + "A0000000031010", // ddddddddddddddddddddddddddddddddddddddddddddddddddddddddd + "A000000003101001", // VISA Credit + "A000000003101002", // VISA Debit + "A0000000032010", // VISA Electron + "A0000000032020", // VISA + "A0000000033010", // VISA Interlink + "A0000000034010", // VISA Specific + "A0000000035010", // VISA Specific + "A0000000036010", // Domestic Visa Cash Stored Value + "A0000000036020", // International Visa Cash Stored Value + "A0000000038002", // VISA Auth, VisaRemAuthen EMV-CAP (DPA) + "A0000000038010", // VISA Plus + "A0000000039010", // VISA Loyalty + "A000000003999910", // VISA Proprietary ATM + // Visa USA + "A000000098", // Debit Card + "A0000000980848", // Debit Card + // Mastercard International + "A00000000401", // MasterCard PayPass + "A0000000041010", // MasterCard Credit + "A00000000410101213", // MasterCard Credit + "A00000000410101215", // MasterCard Credit + "A0000000042010", // MasterCard Specific + "A0000000043010", // MasterCard Specific + "A0000000043060", // Maestro (Debit) + "A000000004306001", // Maestro (Debit) + "A0000000044010", // MasterCard Specific + "A0000000045010", // MasterCard Specific + "A0000000046000", // Cirrus + "A0000000048002", // SecureCode Auth EMV-CAP + "A0000000049999", // MasterCard PayPass + // American Express + "A000000025", + "A0000000250000", + "A00000002501", + "A000000025010402", + "A000000025010701", + "A000000025010801", + // Groupement des Cartes Bancaires "CB" + "A0000000421010", // Cartes Bancaire EMV Card + "A0000000422010", + "A0000000423010", + "A0000000424010", + "A0000000425010", + // JCB CO., LTD. + "A00000006510", // JCB + "A0000000651010", // JCB J Smart Credit + "A0000001544442", // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A. + "F0000000030001", // BRADESCO + "A0000005241010", // RuPay - RuPay + "D5780000021010" // Bankaxept - Bankaxept +}; +const size_t AIDlistLen = sizeof(AIDlist)/sizeof(char*); + +static bool APDULogging = false; +void SetAPDULogging(bool logging) { + APDULogging = logging; +} + static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { emv_tag_dump(tlv, stdout, level); if (is_leaf) { @@ -23,7 +94,7 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { struct tlvdb *t = NULL; t = tlvdb_parse_multi(data, datalen); if (t) { - PrintAndLog("TLV decoded:"); + PrintAndLog("-------------------- TLV decoded --------------------"); tlvdb_visit(t, print_cb, NULL, 0); tlvdb_free(t); @@ -31,3 +102,362 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) { PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); } } + +void TLVPrintFromTLV(struct tlvdb *tlv) { + if (!tlv) + return; + + tlvdb_visit(tlv, print_cb, NULL, 0); +} + +void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { + PrintAndLog("|------------------|--------|-------------------------|"); + PrintAndLog("| AID |Priority| Name |"); + PrintAndLog("|------------------|--------|-------------------------|"); + + struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); + if (!ttmp) + PrintAndLog("| none |"); + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); + const struct tlv *tgName = tlvdb_get_inchild(ttmp, 0x50, NULL); + const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); + if (!tgAID) + break; + PrintAndLog("|%s| %s |%s|", + sprint_hex_inrow_ex(tgAID->value, tgAID->len, 18), + (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", + (tgName) ? sprint_ascii_ex(tgName->value, tgName->len, 25) : " "); + + ttmp = tlvdb_find_next(ttmp, 0x6f); + } + + PrintAndLog("|------------------|--------|-------------------------|"); +} + + +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) { + uint8_t data[APDU_RES_LEN] = {0}; + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + // select APDU + data[0] = 0x00; + data[1] = 0xA4; + data[2] = 0x04; + data[3] = 0x00; + data[4] = AIDLen; + memcpy(&data[5], AID, AIDLen); + + if (ActivateField) + DropField(); + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, AIDLen + 6)); + + int res = ExchangeAPDU14a(data, AIDLen + 6, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + if (res) { + return res; + } + + if (*ResultLen < 2) { + PrintAndLog("SELECT ERROR: returned %d bytes", *ResultLen); + return 5; + } + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("SELECT ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + +int EMVSelectPSE(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; + int res = 0; + switch (PSENum) { + case 1: + param_gethex_to_eol(PSElist[1], 0, buf, sizeof(buf), &len); + break; + case 2: + param_gethex_to_eol(PSElist[0], 0, buf, sizeof(buf), &len); + break; + default: + return -1; + } + + // select + res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); + + return res; +} + +int EMVSearchPSE(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); + + if (!res){ + struct tlvdb *t = NULL; + t = tlvdb_parse_multi(data, datalen); + if (t) { + int retrycnt = 0; + struct tlvdb *ttmp = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0xbf0c, 0x61, 0x00}); + if (!ttmp) + PrintAndLog("PPSE don't have records."); + + 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); + + // retry if error and not returned sw error + if (res && res != 5) { + if (++retrycnt < 3){ + continue; + } else { + // card select error, proxmark error + if (res == 1) { + PrintAndLog("Exit..."); + return 1; + } + + retrycnt = 0; + PrintAndLog("Retry failed [%s]. Skiped...", sprint_hex_inrow(tgAID->value, tgAID->len)); + } + + // next element + ttmp = tlvdb_find_next(ttmp, 0x61); + continue; + } + retrycnt = 0; + + // all is ok + if (decodeTLV){ + PrintAndLog("%s:", sprint_hex_inrow(tgAID->value, tgAID->len)); + TLVPrintFromBuffer(data, datalen); + } + } + + ttmp = tlvdb_find_next(ttmp, 0x61); + } + + tlvdb_free(t); + } else { + PrintAndLog("PPSE ERROR: Can't get TLV from response."); + } + } else { + PrintAndLog("PPSE ERROR: Can't select PPSE AID. Error: %d", res); + } + + if(!LeaveFieldON) + DropField(); + + return res; +} + +int EMVSearch(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}; + size_t datalen = 0; + uint16_t sw = 0; + + int res = 0; + int retrycnt = 0; + for(int i = 0; i < AIDlistLen; i ++) { + param_gethex_to_eol(AIDlist[i], 0, aidbuf, sizeof(aidbuf), &aidlen); + res = EMVSelect((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..."); + return 1; + } + + retrycnt = 0; + PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i]); + } + continue; + } + retrycnt = 0; + + if (res) + continue; + + if (decodeTLV){ + PrintAndLog("%s:", AIDlist[i]); + TLVPrintFromBuffer(data, datalen); + } + } + + return 0; +} + +int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { + // needs to check priority. 0x00 - highest + int prio = 0xffff; + + *AIDlen = 0; + + struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); + if (!ttmp) + return 1; + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); + const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); + + if (!tgAID) + break; + + if (tgPrio) { + int pt = bytes_to_num((uint8_t*)tgPrio->value, (tgPrio->len < 2) ? tgPrio->len : 2); + if (pt < prio) { + prio = pt; + + memcpy(AID, tgAID->value, tgAID->len); + *AIDlen = tgAID->len; + } + } else { + // takes the first application from list wo priority + if (!*AIDlen) { + memcpy(AID, tgAID->value, tgAID->len); + *AIDlen = tgAID->len; + } + } + + ttmp = tlvdb_find_next(ttmp, 0x6f); + } + + 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) { + uint8_t data[APDU_RES_LEN] = {0}; + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + // GPO APDU + data[0] = 0x80; + data[1] = 0xA8; + data[2] = 0x00; + data[3] = 0x00; + data[4] = PDOLLen; + if (PDOL) + memcpy(&data[5], PDOL, PDOLLen); + + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, PDOLLen + 5)); + + int res = ExchangeAPDU14a(data, PDOLLen + 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + if (res) { + return res; + } + + if (*ResultLen < 2) { + PrintAndLog("GPO ERROR: returned %d bytes", *ResultLen); + return 5; + } + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("GPO ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + +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) { + uint8_t data[10] = {0}; + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + // read record APDU + data[0] = 0x00; + data[1] = 0xb2; + data[2] = SFIrec; + data[3] = (SFI << 3) | 0x04; + data[4] = 0; + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, 5)); + + int res = ExchangeAPDU14a(data, 5, false, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + if (res) { + return res; + } + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("Read record ERROR: [%4X] %s", isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + + diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 523b92ac..fdd3ab86 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -5,7 +5,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// EMV core functions +// EMV core functionality //----------------------------------------------------------------------------- #ifndef EMVCORE_H__ @@ -15,14 +15,36 @@ #include #include #include +#include #include "util.h" #include "common.h" #include "ui.h" +#include "cmdhf14a.h" +#include "emv/apduinfo.h" #include "emv/tlv.h" +#include "emv/dol.h" #include "emv/dump.h" #include "emv/emv_tags.h" +#define APDU_RES_LEN 260 +#define APDU_AID_LEN 50 + extern void TLVPrintFromBuffer(uint8_t *data, int datalen); +extern void TLVPrintFromTLV(struct tlvdb *tlv); +extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv); + +extern void SetAPDULogging(bool logging); + +// 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); +// 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); #endif diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 24125cc7..1be91777 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -299,6 +299,40 @@ void tlvdb_free(struct tlvdb *tlvdb) } } +struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + return tlvdb_find(tlvdb->next, tag); +} + +struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + for (; tlvdb; tlvdb = tlvdb->next) { + if (tlvdb->tag.tag == tag) + return tlvdb; + } + + return NULL; +} + +struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) { + int i = 0; + struct tlvdb *tnext = tlvdb; + + while (tnext && tag[i]) { + tnext = tlvdb_find(tnext, tag[i]); + i++; + if (tag[i] && tnext) { + tnext = tnext->children; + } + } + + return tnext; +} + void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) { while (tlvdb->next) { @@ -317,9 +351,8 @@ void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) for (; tlvdb; tlvdb = next) { next = tlvdb->next; - bool is_leaf = (tlvdb->children == NULL); - cb(data, &tlvdb->tag, level, is_leaf); - tlvdb_visit(tlvdb->children, cb, data, level+1); + cb(data, &tlvdb->tag, level, (tlvdb->children == NULL)); + tlvdb_visit(tlvdb->children, cb, data, level + 1); } } @@ -356,6 +389,11 @@ const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const stru return NULL; } +const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) { + tlvdb = tlvdb->children; + return tlvdb_get(tlvdb, tag, prev); +} + unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) { size_t size = tlv->len; diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 5a573566..5b8b6825 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -39,10 +39,15 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); void tlvdb_free(struct tlvdb *tlvdb); +struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]); + void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); +const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); unsigned char *tlv_encode(const struct tlv *tlv, size_t *len); diff --git a/client/scripts/brutesim.lua b/client/scripts/brutesim.lua new file mode 100644 index 00000000..326966c6 --- /dev/null +++ b/client/scripts/brutesim.lua @@ -0,0 +1,229 @@ +desc = [[ + + .-----------------------------------------------------------------. + / .-. .-. \ +| / \ BruteSim / \ | +| |\_. | (bruteforce simulation for multiple tags) | /| | +|\| | /| by |\ | |/| +| `---' | Kenzy Carey | `---' | +| | | | +| |-----------------------------------------------------| | +\ | | / + \ / \ / + `---' `---' +]] +author = [[ Kenzy Carey ]] +usage = [[ + +USAGE: +script run brutesim -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction +option argument description +------ -------- ----------- +-r *see below RFID Tag: the RFID tag to emulate +-f 0-999 Facility Code: The facility code (dfx: country id, 14a: type) +-b 0-65535 Base Card Number: base card number to start from +-c 1-65536 Count: number of cards to try +-t .0-99999, pause Timeout: timeout between cards (use the word 'pause' to wait for user input) +-d up, down Direction: direction to move through card numbers +-h Show this + +*SUPPORTED TAGS: pyramid, awid, fdx, jablotron, noralsy, presco, visa2000, 14a, hid + +EXAMPLE: +script run brutesim -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down +(the above example would bruteforce pyramid tags, starting at 10:1000, ending at 10:991, and waiting 1 second between each card) +]] + +-- I wrote this as i was doing a PACS audit. This is far from complete, but is easily expandable. +-- The idea was based on proxbrute, but i needed more options, and support for different readers. +-- I dont know LUA, so I used Brian Redbeards lf_bulk_program.lua script as a starting point, sorry if its kludgy. + +getopt = require('getopt') -- Used to get get command line arguments +bit32 = require('bit32') -- Used to convert FC/CN to hex + +local function isempty(s) -- Check if a string is empty + return s == nil or s == '' +end + +local function main(args) + + print("") -- Print a blank line to make things look cleaner + + for o, a in getopt.getopt(args, 'r:f:b:c:t:d:h') do -- Populate command like arguments + if o == 'r' then rfidtag = a end + if o == 'f' then facility = a end + if o == 'b' then baseid = a end + if o == 'c' then count = a end + if o == 't' then timeout = a end + if o == 'd' then direction = a end + if o == 'h' then return print(usage) end + end + + if isempty(rfidtag) then -- Check to see if -r argument was passed + print("You must supply the flag -r (rfid tag)") + print(usage) + return + end + -- Check what RFID Tag we are using + if rfidtag == 'pyramid' then -- For eaach RFID Tag: + consolecommand = 'lf pyramid sim' -- Set the console command + rfidtagname = 'Farpointe/Pyramid' -- Set the display name + facilityrequired = 1 -- Set if FC is required + elseif rfidtag == 'awid' then + consolecommand = 'lf awid sim' + rfidtagname = 'AWID' + facilityrequired = 1 + elseif rfidtag == 'fdx' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯ + consolecommand = 'lf fdx sim' + rfidtagname = 'FDX-B' + facilityrequired = 1 + elseif rfidtag == 'jablotron' then + consolecommand = 'lf jablotron sim' + rfidtagname = 'Jablotron' + facilityrequired = 0 + elseif rfidtag == 'noralsy' then + consolecommand = 'lf noralsy sim' + rfidtagname = 'Noralsy' + facilityrequired = 0 + elseif rfidtag == 'presco' then + consolecommand = 'lf presco sim d' + rfidtagname = 'Presco' + facilityrequired = 0 + elseif rfidtag == 'visa2000' then + consolecommand = 'lf visa2000 sim' + rfidtagname = 'Visa2000' + facilityrequired = 0 + elseif rfidtag == '14a' then + consolecommand = 'hf 14a sim' + if facility == "1" then rfidtagname = 'MIFARE Classic' -- Here we use the -f option to read the 14a type instead of the facility code + elseif facility == "2" then rfidtagname = 'MIFARE Ultralight' + elseif facility == "3" then rfidtagname = 'MIFARE Desfire' + elseif facility == "4" then rfidtagname = 'ISO/IEC 14443-4' + elseif facility == "5" then rfidtagname = 'MIFARE Tnp3xxx' + else + print("Invalid 14a type (-f) supplied. Must be 1-5") + print(usage) + return + end + facilityrequired = 0 -- Disable the FC required check, as we used it for type instead of FC + elseif rfidtag == 'hid' then + consolecommand = 'lf hid sim' + rfidtagname = 'HID' + facilityrequired = 1 + else -- Display error and exit out if bad RFID tag was supplied + print("Invalid rfid tag (-r) supplied") + print(usage) + return + end + + if isempty(baseid) then -- Display error and exit out if no starting id is set + print("You must supply the flag -b (base id)") + print(usage) + return + end + + if isempty(count) then -- Display error and exit out of no count is set + print("You must supply the flag -c (count)") + print(usage) + return + end + + if facilityrequired == 1 then -- If FC is required + facilitymessage = " - Facility Code: " -- Add FC to status message + if isempty(facility) then -- If FC was left blank, display warning and set FC to 0 + print("Using 0 for the facility code as -f was not supplied") + facility = 0 + end + else -- If FC is not required + facility = "" -- Clear FC + facilitymessage = "" -- Remove FC from status message + end + + if isempty(timeout) then -- If timeout was not supplied, show warning and set timeout to 0 + print("Using 0 for the timeout as -t was not supplied") + timeout = 0 + end + + if isempty(direction) then -- If direction was not supplied, show warning and set direction to down + print("Using down for direction as -d was not supplied") + direction = 'down' + end + + if tonumber(count) < 1 then + print("Count -c must be set to 1 or higher") + return + else + count = count -1 -- Make our count accurate by removing 1, because math + end + + if direction == 'down' then -- If counting down, set up our for loop to count down + endid = baseid - count + fordirection = -1 + elseif direction == 'up' then -- If counting up, set our for loop to count up + endid = baseid + count + fordirection = 1 + else -- If invalid direction was set, show warning and set up our for loop to count down + print("Invalid direction (-d) supplied, using down") + endid = baseid - count + fordirection = -1 + end + + -- The code below was blatantly stolen from Brian Redbeard's lf_bulk_program.lua script + function toBits(num,bits) + bits = bits or math.max(1, select(2, math.frexp(num))) + local t = {} + for b = bits, 1, -1 do + t[b] = math.fmod(num, 2) + num = math.floor((num - t[b]) / 2) + end + return table.concat(t) + end + + local function evenparity(s) + local _, count = string.gsub(s, "1", "") + local p = count % 2 + if (p == 0) then + return(false) + else + return(true) + end + end + + local function isempty(s) + return s == nil or s == '' + end + + local function cardHex(i,f) + fac = bit32.lshift(f,16) + id = bit32.bor(i, fac) + stream=toBits(id,26) + high = evenparity(string.sub(stream,0,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bit32.bor(bit32.lshift(id,1), low) + bits = bit32.bor(bits, bit32.lshift(high,25)) + preamble = bit32.bor(0, bit32.lshift(1,5)) + bits = bit32.bor(bits, bit32.lshift(1,26)) + return ("%04x%08x"):format(preamble,bits) + end + -- End stolen code + + + print("") -- Display status message + print("BruteForcing "..rfidtagname..""..facilitymessage..""..facility.." - CardNumber Start: "..baseid.." - CardNumber End: "..endid.." - TimeOut: "..timeout) + print("") + for cardnum = baseid,endid,fordirection do -- Loop through for each count (-c) + if rfidtag == 'hid' then cardnum = cardHex(cardnum, facility) end -- If rfid tag is set to HID, convert card to HEX using the stolen code above + core.console(consolecommand..' '..facility..' '..cardnum) -- Send command to proxmark + if timeout == 'pause' then -- If timeout is set to pause, wait for user input + print("Press enter to continue ...") + io.read() + else -- Otherwise sleep for timeout duration + os.execute("sleep "..timeout.."") + end + end + core.console('hw ping') -- Ping the proxmark to stop emulation and see if its still responding + +end -- Go bye bye + + +main(args) -- Do the thing diff --git a/client/util.c b/client/util.c index a1caafdb..92b0e7a6 100644 --- a/client/util.c +++ b/client/util.c @@ -154,6 +154,28 @@ char *sprint_hex(const uint8_t *data, const size_t len) { return buf; } +char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + + int maxLen = ( len > 1024/2) ? 1024/2 : len; + static char buf[1024] = {0}; + char *tmp = buf; + size_t i; + + for (i = 0; i < maxLen; ++i, tmp += 2) + sprintf(tmp, "%02x", (unsigned int) data[i]); + + i *= 2; + int minStrLen = min_str_len > i ? min_str_len : 0; + for(; i < minStrLen; i++, tmp += 1) + sprintf(tmp, " "); + + return buf; +} + +char *sprint_hex_inrow(const uint8_t *data, const size_t len) { + return sprint_hex_inrow_ex(data, len, 0); +} + char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) { // make sure we don't go beyond our char array memory int max_len; @@ -210,7 +232,7 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) { return buf; } -char *sprint_ascii(const uint8_t *data, const size_t len) { +char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { static char buf[1024]; char *tmp = buf; memset(buf, 0x00, 1024); @@ -221,9 +243,18 @@ char *sprint_ascii(const uint8_t *data, const size_t len) { tmp[i] = ((c < 32) || (c == 127)) ? '.' : c; ++i; } + + int minStrLen = min_str_len > i ? min_str_len : 0; + for(; i < minStrLen; ++i) + tmp[i] = ' '; + return buf; } +char *sprint_ascii(const uint8_t *data, const size_t len) { + return sprint_ascii_ex(data, len, 0); +} + void num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { while (len--) { @@ -498,8 +529,10 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxd int indx = bg; while (line[indx]) { - if (line[indx] == '\t' || line[indx] == ' ') + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; continue; + } if (isxdigit(line[indx])) { buf[strlen(buf) + 1] = 0x00; diff --git a/client/util.h b/client/util.h index 8bab8cfd..e9c48b03 100644 --- a/client/util.h +++ b/client/util.h @@ -37,10 +37,13 @@ extern void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byte extern void print_hex(const uint8_t * data, const size_t len); extern char *sprint_hex(const uint8_t * data, const size_t len); +extern char *sprint_hex_inrow(const uint8_t *data, const size_t len); +extern char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern char *sprint_bin(const uint8_t * data, const size_t len); extern char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks); extern char *sprint_hex_ascii(const uint8_t *data, const size_t len); extern char *sprint_ascii(const uint8_t *data, const size_t len); +extern char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); extern uint64_t bytes_to_num(uint8_t* src, size_t len);