Merge branch 'master' into argtable

This commit is contained in:
Oleg Moiseenko 2017-11-28 15:20:50 +02:00 committed by GitHub
commit e38a6bcddf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1703 additions and 89 deletions

View file

@ -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 proxmark command line parameter `flush` to `-f` or `-flush` (Merlok)
- Changed `hf 14a reader` to just reqest-anticilission-select sequence (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 `hf 14a raw` - works with LED's and some exchange logic (Merlok)
- Changed TLV parser messages to more convenient (Merlok)
- `hf 14a` commands works via argtable3 commandline parsing library (Merlok) - `hf 14a` commands works via argtable3 commandline parsing library (Merlok)
### Fixed ### Fixed
@ -39,6 +40,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 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` - exchange apdu via iso1443-4 (Merlok)
- Added to `hf 14a apdu` - apdu and tlv results parser (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] ## [3.0.1][2017-06-08]

View file

@ -1,56 +1,37 @@
NOTICE: # proxmark3: the official Proxmark repository!
(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 The proxmark3 is a powerful general purpose RFID tool, the size of a deck
of cards, designed to snoop, listen and emulate everything from of cards, designed to snoop, listen and emulate everything from
Low Frequency (125kHz) to High Frequency (13.56MHz) tags. **Low Frequency (125kHz)** to **High Frequency (13.56MHz)** tags.
This repository contains enough software, logic (for the FPGA), and design This repository contains enough software, logic (for the FPGA), and design
documentation for the hardware that you could, at least in theory, documentation for the hardware that you could, at least in theory,
do something useful with a proxmark3. do something useful with a proxmark3.
RESOURCES: ## Resources
* This repository! * [This repository!](https://github.com/Proxmark/proxmark3)
https://github.com/Proxmark/proxmark3 * [The Wiki](https://github.com/Proxmark/proxmark3/wiki)
* [The GitHub Pages website](http://proxmark.github.io/proxmark3/)
* The Wiki * [The Forum](http://www.proxmark.org/forum)
https://github.com/Proxmark/proxmark3/wiki * 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)
* 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: ## Development
The tools required to build or run the project will vary depending on The tools required to build or run the project will vary depending on
your operating system. Please refer to the Wiki for details. your operating system. Please refer to [the wiki](https://github.com/Proxmark/proxmark3/wiki) for details.
* https://github.com/Proxmark/proxmark3/wiki ## Obtaining hardware
OBTAINING HARDWARE:
The Proxmark3 is available for purchase (assembled and tested) from the The Proxmark3 is available for purchase (assembled and tested) from the
following locations: following locations:
* https://proxmark3.com/ - RyscCorp (us) * [RyscCorp](https://proxmark3.com/) (US)
* http://www.elechouse.com/ - Elechouse (HK) * [Elechouse](http://www.elechouse.com/) (HK)
* https://lab401.com/ - Lab401 (FR) * [Lab401](https://lab401.com/) (FR)
* http://www.rfxsecure.com/ - RFxSecure (SG) * [RFxSecure](http://www.rfxsecure.com/) (SG)
* http://proxmark3.tictail.com/ - IceSQL (SE) * [IceSQL](http://proxmark3.tictail.com/) (SE)
Most of the ultra-low-volume contract assemblers could put Most of the ultra-low-volume contract assemblers could put
something like this together with a reasonable yield. A run of around something like this together with a reasonable yield. A run of around
@ -70,7 +51,7 @@ The printed circuit board artwork is also available, as Gerbers and an
Excellon drill file. Excellon drill file.
LICENSING: ## License
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View file

@ -189,33 +189,15 @@ void iso14a_set_trigger(bool enable) {
void iso14a_set_timeout(uint32_t timeout) { void iso14a_set_timeout(uint32_t timeout) {
iso14a_timeout = timeout; iso14a_timeout = timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8);
if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106);
} }
static void iso14a_set_ATS_timeout(uint8_t *ats) { uint32_t iso14a_get_timeout(void) {
return iso14a_timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8);
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));
}
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Generate the parity value for a byte sequence // 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[]. // Transmit the command (to the tag) that was placed in ToSend[].
// Parameter timing: // Parameter timing:
// if NULL: transfer at next possible time, taking into account // 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: transfer immediately and return time of transfer
// if != 0: delay transfer until time specified // if != 0: delay transfer until time specified
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
@ -1685,6 +1667,59 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity)
return Demod.len; 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 // performs iso14443a anticollision (optional) and card select procedure
// fills the uid and cuid pointer unless NULL // fills the uid and cuid pointer unless NULL
// fills the card info record 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) // 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 // 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) { 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_all[] = { 0x93,0x20 };
uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 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 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; p_hi14a_card->ats_len = 0;
} }
// Broadcast for a card, WUPA (0x52) will force response from all cards in the field if (!GetATQA(resp, resp_par)) {
ReaderTransmitBitsPar(wupa, 7, NULL, NULL); return 0;
}
// Receive the ATQA
if(!ReaderReceive(resp, resp_par)) return 0;
if(p_hi14a_card) { if(p_hi14a_card) {
memcpy(p_hi14a_card->atqa, resp, 2); 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 // reset the PCB block number
iso14_pcb_blocknum = 0; iso14_pcb_blocknum = 0;
// set default timeout based on ATS // set default timeout and delay next transfer based on ATS
iso14a_set_ATS_timeout(resp); iso14a_set_ATS_times(resp);
} }
return 1; return 1;
} }

View file

@ -292,6 +292,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers
strncat(dst, "\n", len - strlen(dst) - 1); strncat(dst, "\n", len - strlen(dst) - 1);
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// timer lib // timer lib
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -312,6 +313,7 @@ void StartTickCount()
// note: worst case precision is approx 2.5% // note: worst case precision is approx 2.5%
} }
/* /*
* Get the current count. * Get the current count.
*/ */
@ -319,6 +321,7 @@ uint32_t RAMFUNC GetTickCount(){
return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2;
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// microseconds timer // microseconds timer
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -344,10 +347,12 @@ void StartCountUS()
AT91C_BASE_TCB->TCB_BCR = 1; AT91C_BASE_TCB->TCB_BCR = 1;
} }
uint32_t RAMFUNC GetCountUS(){ uint32_t RAMFUNC GetCountUS(){
return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV * 2) / 3); //was /15) * 10); return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV * 2) / 3); //was /15) * 10);
} }
static uint32_t GlobalUsCounter = 0; static uint32_t GlobalUsCounter = 0;
uint32_t RAMFUNC GetDeltaCountUS(){ uint32_t RAMFUNC GetDeltaCountUS(){
@ -402,7 +407,7 @@ void StartCountSspClk()
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN; // enable TC2 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 go high (start of frame)
while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low 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). // (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 // 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. // 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) { void ResetSspClk(void) {
//enable clock of timer and software trigger //enable clock of timer and software trigger
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; 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; AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
while (AT91C_BASE_TC2->TC_CV > 0); while (AT91C_BASE_TC2->TC_CV > 0);
} }
uint32_t RAMFUNC GetCountSspClk(){ uint32_t RAMFUNC GetCountSspClk(){
uint32_t tmp_count; uint32_t tmp_count;
tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; 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 // Timer for bitbanging, or LF stuff when you need a very precis timer
// 1us = 1.5ticks // 1us = 1.5ticks
@ -464,6 +475,7 @@ void StartTicks(void){
while (AT91C_BASE_TC1->TC_CV > 0); while (AT91C_BASE_TC1->TC_CV > 0);
} }
// Wait - Spindelay in ticks. // Wait - Spindelay in ticks.
// if called with a high number, this will trigger the WDT... // if called with a high number, this will trigger the WDT...
void WaitTicks(uint32_t ticks){ void WaitTicks(uint32_t ticks){
@ -471,32 +483,43 @@ void WaitTicks(uint32_t ticks){
ticks += GET_TICKS; ticks += GET_TICKS;
while (GET_TICKS < ticks); while (GET_TICKS < ticks);
} }
// Wait / Spindelay in us (microseconds) // Wait / Spindelay in us (microseconds)
// 1us = 1.5ticks. // 1us = 1.5ticks.
void WaitUS(uint16_t us){ void WaitUS(uint16_t us){
if ( us == 0 ) return; if ( us == 0 ) return;
WaitTicks( (uint32_t)(us * 1.5) ); WaitTicks( (uint32_t)(us * 1.5) );
} }
void WaitMS(uint16_t ms){ void WaitMS(uint16_t ms){
if (ms == 0) return; if (ms == 0) return;
WaitTicks( (uint32_t)(ms * 1500) ); WaitTicks( (uint32_t)(ms * 1500) );
} }
// Starts Clock and waits until its reset // Starts Clock and waits until its reset
void ResetTicks(void){ void ResetTicks(void){
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC1->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); while (AT91C_BASE_TC1->TC_CV > 0);
} }
void ResetTimer(AT91PS_TC timer){ void ResetTimer(AT91PS_TC timer){
timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
while(timer->TC_CV > 0) ; while(timer->TC_CV > 0) ;
} }
// stop clock // stop clock
void StopTicks(void){ void StopTicks(void){
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
} }
static uint64_t next_random = 1; static uint64_t next_random = 1;
/* Generates a (non-cryptographically secure) 32-bit random number. /* Generates a (non-cryptographically secure) 32-bit random number.
@ -512,4 +535,3 @@ uint32_t prand() {
next_random = next_random * 6364136223846793005 + 1; next_random = next_random * 6364136223846793005 + 1;
return (uint32_t)(next_random >> 32) % 0xffffffff; return (uint32_t)(next_random >> 32) % 0xffffffff;
} }

View file

@ -113,7 +113,9 @@ CMDSRCS = crapto1/crapto1.c\
emv/dump.c\ emv/dump.c\
emv/tlv.c\ emv/tlv.c\
emv/emv_tags.c\ emv/emv_tags.c\
emv/dol.c\
emv/emvcore.c\ emv/emvcore.c\
emv/cmdemv.c\
cmdhf.c \ cmdhf.c \
cmdhf14a.c \ cmdhf14a.c \
cmdhf14b.c \ cmdhf14b.c \

View file

@ -30,6 +30,7 @@
#include "cmdhfmfu.h" #include "cmdhfmfu.h"
#include "cmdhftopaz.h" #include "cmdhftopaz.h"
#include "protocols.h" #include "protocols.h"
#include "emv/cmdemv.h"
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
@ -703,6 +704,7 @@ static command_t CommandTable[] =
{"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"},
{"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"},
{"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, {"epa", CmdHFEPA, 1, "{ German Identification Card... }"},
{"emv", CmdHFEMV, 1, "{ EMV cards... }"},
{"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"},
{"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"},
{"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"},

View file

@ -643,7 +643,7 @@ void DropField() {
SendCommand(&c); 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; uint16_t cmdc = 0;
if (activateField) { if (activateField) {
@ -681,6 +681,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
*dataoutlen = iLen - 2; *dataoutlen = iLen - 2;
if (*dataoutlen < 0) if (*dataoutlen < 0)
*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); memcpy(dataout, recv, *dataoutlen);
if(!iLen) { if(!iLen) {
@ -750,7 +756,7 @@ int CmdHF14AAPDU(const char *cmd) {
PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen));
int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, &datalen); int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen);
if (res) if (res)
return res; return res;

View file

@ -31,6 +31,6 @@ int CmdHF14ASnoop(const char *Cmd);
char* getTagInfo(uint8_t uid); char* getTagInfo(uint8_t uid);
extern void DropField(); 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 #endif

552
client/emv/cmdemv.c Normal file
View file

@ -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] <HEX applet AID>\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;
}

31
client/emv/cmdemv.h Normal file
View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#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

135
client/emv/dol.c Normal file
View file

@ -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 <config.h>
#endif
#include "emv/dol.h"
#include "emv/tlv.h"
#include <stdlib.h>
#include <string.h>
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;
}

25
client/emv/dol.h Normal file
View file

@ -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 <stddef.h>
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

View file

@ -18,8 +18,8 @@
#endif #endif
#include "dump.h" #include "dump.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} #define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");}

View file

@ -21,5 +21,6 @@
void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); 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(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 #endif

View file

@ -29,6 +29,7 @@ enum emv_tag_t {
EMV_TAG_BITMASK, EMV_TAG_BITMASK,
EMV_TAG_DOL, EMV_TAG_DOL,
EMV_TAG_CVM_LIST, EMV_TAG_CVM_LIST,
EMV_TAG_AFL,
EMV_TAG_STRING, EMV_TAG_STRING,
EMV_TAG_NUMERIC, EMV_TAG_NUMERIC,
EMV_TAG_YYMMDD, 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, 3), "Issuer authentication is supported" },
{ EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" },
{ EMV_BIT(1, 1), "CDA supported" }, { 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, 7), "Reserved for use by the EMV Contactless Specifications" },
{ EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" },
{ EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(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, 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[] = { static const struct emv_tag emv_tags[] = {
{ 0x00 , "Unknown ???" }, { 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" }, { 0x4f , "Application Dedicated File (ADF) Name" },
{ 0x50 , "Application Label", EMV_TAG_STRING }, { 0x50 , "Application Label", EMV_TAG_STRING },
{ 0x56 , "Track 1 Data" }, { 0x56 , "Track 1 Data" },
@ -147,12 +184,13 @@ static const struct emv_tag emv_tags[] = {
{ 0x91 , "Issuer Authentication Data" }, { 0x91 , "Issuer Authentication Data" },
{ 0x92 , "Issuer Public Key Remainder" }, { 0x92 , "Issuer Public Key Remainder" },
{ 0x93 , "Signed Static Application Data" }, { 0x93 , "Signed Static Application Data" },
{ 0x94 , "Application File Locator (AFL)" }, { 0x94 , "Application File Locator (AFL)", EMV_TAG_AFL },
{ 0x95 , "Terminal Verification Results" }, { 0x95 , "Terminal Verification Results" },
{ 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, { 0x9a , "Transaction Date", EMV_TAG_YYMMDD },
{ 0x9c , "Transaction Type" }, { 0x9c , "Transaction Type" },
{ 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC },
{ 0x9f03, "Amount, Other (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 }, { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC },
{ 0x9f08, "Application Version Number" }, { 0x9f08, "Application Version Number" },
{ 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR },
@ -168,6 +206,7 @@ static const struct emv_tag emv_tags[] = {
{ 0x9f21, "Transaction Time" }, { 0x9f21, "Transaction Time" },
{ 0x9f26, "Application Cryptogram" }, { 0x9f26, "Application Cryptogram" },
{ 0x9f27, "Cryptogram Information Data" }, { 0x9f27, "Cryptogram Information Data" },
{ 0x9f2a, "Kernel Identifier" },
{ 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" },
{ 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" },
{ 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" },
@ -193,9 +232,12 @@ static const struct emv_tag emv_tags[] = {
{ 0x9f63, "PUNATC(Track1)" }, { 0x9f63, "PUNATC(Track1)" },
{ 0x9f64, "NATC(Track1)" }, { 0x9f64, "NATC(Track1)" },
{ 0x9f65, "PCVC3(Track2)" }, { 0x9f65, "PCVC3(Track2)" },
{ 0x9f66, "PUNATC(Track2)" }, { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ },
{ 0x9f67, "NATC(Track2)" }, { 0x9f67, "NATC(Track2) / MSD Offset" },
{ 0x9f69, "Card Authentication Related Data" },
{ 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC },
{ 0x9f6b, "Track 2 Data" }, { 0x9f6b, "Track 2 Data" },
{ 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ },
{ 0xa5 , "File Control Information (FCI) Proprietary Template" }, { 0xa5 , "File Control Information (FCI) Proprietary Template" },
{ 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, { 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); PRINT_INDENT(level);
fprintf(f, "\tByte %u (%02x)\n", byte, val); fprintf(f, "\tByte %u (%02x)\n", byte, val);
for (bit = 8; bit > 0; bit--, val <<= 1) { for (bit = 8; bit > 0; bit--, val <<= 1) {
if (val & 0x80) if (val & 0x80){
PRINT_INDENT(level); PRINT_INDENT(level);
fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1],
bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown");
}
if (bits->bit == EMV_BIT(byte, bit)) if (bits->bit == EMV_BIT(byte, bit))
bits ++; 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) 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 '"); fprintf(f, "\tString value '");
fwrite(tlv->value, 1, tlv->len, f); fwrite(tlv->value, 1, tlv->len, f);
PRINT_INDENT(level);
fprintf(f, "'\n"); 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) bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level)
{ {
if (!tlv) { 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); const struct emv_tag *tag = emv_get_tag(tlv);
PRINT_INDENT(level); 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) { switch (tag->type) {
case EMV_TAG_GENERIC: case EMV_TAG_GENERIC:
fprintf(f, "\n");
break; break;
case EMV_TAG_BITMASK: case EMV_TAG_BITMASK:
fprintf(f, "\n");
emv_tag_dump_bitmask(tlv, tag, f, level); emv_tag_dump_bitmask(tlv, tag, f, level);
break; break;
case EMV_TAG_DOL: case EMV_TAG_DOL:
fprintf(f, "\n");
emv_tag_dump_dol(tlv, tag, f, level); emv_tag_dump_dol(tlv, tag, f, level);
break; break;
case EMV_TAG_CVM_LIST: case EMV_TAG_CVM_LIST:
fprintf(f, "\n");
emv_tag_dump_cvm_list(tlv, tag, f, level); emv_tag_dump_cvm_list(tlv, tag, f, level);
break; break;
case EMV_TAG_AFL:
fprintf(f, "\n");
emv_tag_dump_afl(tlv, tag, f, level);
break;
case EMV_TAG_STRING: case EMV_TAG_STRING:
emv_tag_dump_string(tlv, tag, f, level); emv_tag_dump_string(tlv, tag, f, level);
break; break;

View file

@ -10,6 +10,77 @@
#include "emvcore.h" #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) { static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) {
emv_tag_dump(tlv, stdout, level); emv_tag_dump(tlv, stdout, level);
if (is_leaf) { if (is_leaf) {
@ -23,7 +94,7 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) {
struct tlvdb *t = NULL; struct tlvdb *t = NULL;
t = tlvdb_parse_multi(data, datalen); t = tlvdb_parse_multi(data, datalen);
if (t) { if (t) {
PrintAndLog("TLV decoded:"); PrintAndLog("-------------------- TLV decoded --------------------");
tlvdb_visit(t, print_cb, NULL, 0); tlvdb_visit(t, print_cb, NULL, 0);
tlvdb_free(t); tlvdb_free(t);
@ -31,3 +102,362 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) {
PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); 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;
}

View file

@ -5,7 +5,7 @@
// at your option, any later version. See the LICENSE.txt file for the text of // at your option, any later version. See the LICENSE.txt file for the text of
// the license. // the license.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// EMV core functions // EMV core functionality
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#ifndef EMVCORE_H__ #ifndef EMVCORE_H__
@ -15,14 +15,36 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include <string.h>
#include "util.h" #include "util.h"
#include "common.h" #include "common.h"
#include "ui.h" #include "ui.h"
#include "cmdhf14a.h"
#include "emv/apduinfo.h"
#include "emv/tlv.h" #include "emv/tlv.h"
#include "emv/dol.h"
#include "emv/dump.h" #include "emv/dump.h"
#include "emv/emv_tags.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 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 #endif

View file

@ -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) void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other)
{ {
while (tlvdb->next) { 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) { for (; tlvdb; tlvdb = next) {
next = tlvdb->next; next = tlvdb->next;
bool is_leaf = (tlvdb->children == NULL); cb(data, &tlvdb->tag, level, (tlvdb->children == NULL));
cb(data, &tlvdb->tag, level, is_leaf); tlvdb_visit(tlvdb->children, cb, data, level + 1);
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; 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) unsigned char *tlv_encode(const struct tlv *tlv, size_t *len)
{ {
size_t size = tlv->len; size_t size = tlv->len;

View file

@ -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); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len);
void tlvdb_free(struct tlvdb *tlvdb); 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_add(struct tlvdb *tlvdb, struct tlvdb *other);
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); 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(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); 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); unsigned char *tlv_encode(const struct tlv *tlv, size_t *len);

229
client/scripts/brutesim.lua Normal file
View file

@ -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

View file

@ -154,6 +154,28 @@ char *sprint_hex(const uint8_t *data, const size_t len) {
return buf; 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) { 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 // make sure we don't go beyond our char array memory
int max_len; int max_len;
@ -210,7 +232,7 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) {
return buf; 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]; static char buf[1024];
char *tmp = buf; char *tmp = buf;
memset(buf, 0x00, 1024); 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; tmp[i] = ((c < 32) || (c == 127)) ? '.' : c;
++i; ++i;
} }
int minStrLen = min_str_len > i ? min_str_len : 0;
for(; i < minStrLen; ++i)
tmp[i] = ' ';
return buf; 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) void num_to_bytes(uint64_t n, size_t len, uint8_t* dest)
{ {
while (len--) { while (len--) {

View file

@ -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 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(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(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_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_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(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 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); extern uint64_t bytes_to_num(uint8_t* src, size_t len);