mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-03-12 04:35:36 -07:00
Emv commands work with smartcard interface (RfidResearchGroup PR67 by @Merlokk) (#743)
* replace 'hf emv' commands by 'emv' commands * Enable smartcard commands by default (-DWITH_SMARTCARD) * update i2c.c from RfidResearchGroup repository * update smartcard.c from RfidResearchGroup repository
This commit is contained in:
parent
968ad67280
commit
8d7d7b6187
@ -22,6 +22,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||
- Added `lf hitag reader 03` - read block (instead of pages)
|
||||
- Added `lf hitag reader 04` - read block (instead of pages)
|
||||
- Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (Merlok)
|
||||
- Added `emv` commmands working for both contactless and smart cards (Merlok)
|
||||
|
||||
## [v3.1.0][2018-10-10]
|
||||
|
||||
|
122
armsrc/i2c.c
122
armsrc/i2c.c
@ -24,7 +24,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
// 定义连接引脚
|
||||
#define GPIO_RST AT91C_PIO_PA1
|
||||
#define GPIO_SCL AT91C_PIO_PA5
|
||||
#define GPIO_SDA AT91C_PIO_PA7
|
||||
@ -49,14 +48,41 @@ static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) {
|
||||
for (c = delay * 2; c; c--) {};
|
||||
}
|
||||
|
||||
// 通讯延迟函数 communication delay function
|
||||
// communication delay functions
|
||||
#define I2C_DELAY_1CLK I2CSpinDelayClk(1)
|
||||
#define I2C_DELAY_2CLK I2CSpinDelayClk(2)
|
||||
#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x))
|
||||
|
||||
|
||||
#define ISO7618_MAX_FRAME 255
|
||||
|
||||
// try i2c bus recovery at 100kHz = 5uS high, 5uS low
|
||||
static void I2C_recovery(void) {
|
||||
|
||||
DbpString("Performing i2c bus recovery");
|
||||
|
||||
// reset I2C
|
||||
SDA_H; SCL_H;
|
||||
|
||||
//9nth cycle acts as NACK
|
||||
for (int i = 0; i < 10; i++) {
|
||||
SCL_H; WaitUS(5);
|
||||
SCL_L; WaitUS(5);
|
||||
}
|
||||
|
||||
//a STOP signal (SDA from low to high while CLK is high)
|
||||
SDA_L; WaitUS(5);
|
||||
SCL_H; WaitUS(2);
|
||||
SDA_H; WaitUS(2);
|
||||
|
||||
bool isok = (SCL_read && SDA_read);
|
||||
if (!SDA_read)
|
||||
DbpString("I2C bus recovery error: SDA still LOW");
|
||||
if (!SCL_read)
|
||||
DbpString("I2C bus recovery error: SCL still LOW");
|
||||
if (isok)
|
||||
DbpString("I2C bus recovery complete");
|
||||
}
|
||||
|
||||
static void I2C_init(void) {
|
||||
// Configure reset pin
|
||||
AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; // disable pull up resistor
|
||||
@ -72,10 +98,13 @@ static void I2C_init(void) {
|
||||
// configure all three pins as output, controlled by PIOA
|
||||
AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
|
||||
AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
|
||||
|
||||
bool isok = (SCL_read && SDA_read);
|
||||
if ( !isok )
|
||||
I2C_recovery();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 设置复位状态
|
||||
// set the reset state
|
||||
static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) {
|
||||
if (LineRST)
|
||||
@ -94,19 +123,19 @@ static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA
|
||||
LOW(GPIO_SDA);
|
||||
}
|
||||
|
||||
// 复位进入主程序
|
||||
// Reset the SIM_Adapter, then enter the main program
|
||||
// Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter.
|
||||
static void I2C_Reset_EnterMainProgram(void) {
|
||||
I2C_SetResetStatus(0, 0, 0); // 拉低复位线
|
||||
SpinDelay(30);
|
||||
I2C_SetResetStatus(1, 0, 0); // 解除复位
|
||||
SpinDelay(30);
|
||||
I2C_SetResetStatus(1, 1, 1); // 拉高数据线
|
||||
SpinDelay(10);
|
||||
StartTicks();
|
||||
I2C_init();
|
||||
I2C_SetResetStatus(0, 0, 0);
|
||||
WaitMS(30);
|
||||
I2C_SetResetStatus(1, 0, 0);
|
||||
WaitMS(30);
|
||||
I2C_SetResetStatus(1, 1, 1);
|
||||
WaitMS(10);
|
||||
}
|
||||
|
||||
// 等待时钟变高
|
||||
// Wait for the clock to go High.
|
||||
static bool WaitSCL_H_delay(uint32_t delay) {
|
||||
while (delay--) {
|
||||
@ -184,7 +213,8 @@ static void I2C_SendByte(uint8_t data) {
|
||||
uint8_t bits = 8;
|
||||
|
||||
while (bits--) {
|
||||
SCL_L; I2C_DELAY_1CLK;
|
||||
SCL_L;
|
||||
I2C_DELAY_1CLK;
|
||||
|
||||
if (data & 0x80)
|
||||
SDA_H;
|
||||
@ -205,7 +235,6 @@ static void I2C_SendByte(uint8_t data) {
|
||||
}
|
||||
|
||||
bool I2C_is_available(void) {
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
if (!I2C_Start()) // some other device is active on the bus
|
||||
return true;
|
||||
@ -219,14 +248,13 @@ bool I2C_is_available(void) {
|
||||
}
|
||||
|
||||
#ifdef WITH_SMARTCARD
|
||||
// 复位进入引导模式
|
||||
// Reset the SIM_Adapter, then enter the bootloader program
|
||||
// Reserve£ºFor firmware update.
|
||||
static void I2C_Reset_EnterBootloader(void) {
|
||||
I2C_SetResetStatus(0, 1, 1); // 拉低复位线
|
||||
SpinDelay(100);
|
||||
I2C_SetResetStatus(1, 1, 1); // 解除复位
|
||||
SpinDelay(10);
|
||||
I2C_SetResetStatus(0, 1, 1);
|
||||
WaitMS(100);
|
||||
I2C_SetResetStatus(1, 1, 1);
|
||||
WaitMS(10);
|
||||
}
|
||||
|
||||
// Wait max 300ms or until SCL goes LOW.
|
||||
@ -238,7 +266,7 @@ static bool WaitSCL_L_300ms(void) {
|
||||
if (!SCL_read)
|
||||
return true;
|
||||
|
||||
SpinDelay(1);
|
||||
WaitMS(1);
|
||||
}
|
||||
return (delay == 0);
|
||||
}
|
||||
@ -303,7 +331,7 @@ static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) {
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return false;
|
||||
//[C0]
|
||||
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
@ -323,7 +351,6 @@ static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 写入1字节数据 (待写入数据,待写入地址,器件类型)
|
||||
// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ).
|
||||
static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) {
|
||||
bool bBreak = true;
|
||||
@ -354,7 +381,6 @@ static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_addre
|
||||
return true;
|
||||
}
|
||||
|
||||
// 写入1串数据(待写入数组地址,待写入长度,待写入地址,器件类型)
|
||||
//Sends a string of data (Array, length, command to be written , SlaveDevice address ).
|
||||
// len = uint8 (max buffer to write 256bytes)
|
||||
static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
|
||||
@ -393,7 +419,6 @@ static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint
|
||||
return true;
|
||||
}
|
||||
|
||||
// 读出1串数据(存放读出数据,待读出长度,带读出地址,器件类型)
|
||||
// read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ).
|
||||
// len = uint8 (max buffer to read 256bytes)
|
||||
static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
|
||||
@ -403,7 +428,7 @@ static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, ui
|
||||
|
||||
// extra wait 500us (514us measured)
|
||||
// 200us (xx measured)
|
||||
SpinDelayUs(600);
|
||||
WaitUS(600);
|
||||
bool bBreak = true;
|
||||
uint16_t readcount = 0;
|
||||
|
||||
@ -462,6 +487,7 @@ static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, ui
|
||||
}
|
||||
|
||||
I2C_Stop();
|
||||
|
||||
// return bytecount - first byte (which is length byte)
|
||||
return --readcount;
|
||||
}
|
||||
@ -481,12 +507,10 @@ static int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb,
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// msb
|
||||
I2C_SendByte(msb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// lsb
|
||||
I2C_SendByte(lsb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
@ -543,12 +567,10 @@ static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, ui
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// msb
|
||||
I2C_SendByte(msb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// lsb
|
||||
I2C_SendByte(lsb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
@ -577,7 +599,6 @@ static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, ui
|
||||
void I2C_print_status(void) {
|
||||
DbpString("Smart card module (ISO 7816)");
|
||||
uint8_t resp[] = {0,0,0,0};
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN);
|
||||
if ( len > 0 )
|
||||
@ -624,8 +645,6 @@ static bool GetATR(smart_card_atr_t *card_ptr) {
|
||||
// Send ATR
|
||||
// start [C0 01] stop start C1 len aa bb cc stop]
|
||||
I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN);
|
||||
uint8_t cmd[1] = {1};
|
||||
LogTrace(cmd, 1, 0, 0, NULL, true);
|
||||
|
||||
// wait for sim card to answer.
|
||||
// 1byte = 1ms, max frame 256bytes. Should wait 256ms at least just in case.
|
||||
@ -659,19 +678,6 @@ static bool GetATR(smart_card_atr_t *card_ptr) {
|
||||
}
|
||||
}
|
||||
|
||||
// for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr
|
||||
if (len == 1) {
|
||||
|
||||
uint8_t data[1] = {0};
|
||||
I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
|
||||
|
||||
if ( !I2C_WaitForSim() )
|
||||
return false;
|
||||
|
||||
uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
|
||||
len = len + len2;
|
||||
}
|
||||
|
||||
card_ptr->atr_len = len;
|
||||
LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false);
|
||||
|
||||
@ -683,7 +689,6 @@ void SmartCardAtr(void) {
|
||||
LED_D_ON();
|
||||
clear_trace();
|
||||
set_tracing(true);
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
bool isOK = GetATR( &card );
|
||||
cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
|
||||
@ -706,10 +711,9 @@ void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) {
|
||||
|
||||
if ((flags & SC_CONNECT)) {
|
||||
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
|
||||
if ( !(flags & SC_NO_SELECT) ) {
|
||||
if (flags & SC_SELECT) {
|
||||
smart_card_atr_t card;
|
||||
bool gotATR = GetATR( &card );
|
||||
//cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
|
||||
@ -725,18 +729,21 @@ void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) {
|
||||
// Send raw bytes
|
||||
// asBytes = A0 A4 00 00 02
|
||||
// arg1 = len 5
|
||||
I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
|
||||
|
||||
if ( !I2C_WaitForSim() )
|
||||
goto OUT;
|
||||
bool res = I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
|
||||
if ( !res && MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
|
||||
// read bytes from module
|
||||
len = ISO7618_MAX_FRAME;
|
||||
sc_rx_bytes(resp, &len);
|
||||
LogTrace(resp, len, 0, 0, NULL, false);
|
||||
res = sc_rx_bytes(resp, &len);
|
||||
if ( res ) {
|
||||
LogTrace(resp, len, 0, 0, NULL, false);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
OUT:
|
||||
cmd_send(CMD_ACK, len, 0, 0, resp, len);
|
||||
BigBuf_free();
|
||||
set_tracing(false);
|
||||
LEDsoff();
|
||||
}
|
||||
@ -749,7 +756,6 @@ void SmartCardUpgrade(uint64_t arg0) {
|
||||
// write. Sector0, with 11,22,33,44
|
||||
// erase is 128bytes, and takes 50ms to execute
|
||||
|
||||
I2C_init();
|
||||
I2C_Reset_EnterBootloader();
|
||||
|
||||
bool isOK = true;
|
||||
@ -777,7 +783,7 @@ void SmartCardUpgrade(uint64_t arg0) {
|
||||
}
|
||||
|
||||
// writing takes time.
|
||||
SpinDelay(50);
|
||||
WaitMS(50);
|
||||
|
||||
// read
|
||||
res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT);
|
||||
@ -799,6 +805,7 @@ void SmartCardUpgrade(uint64_t arg0) {
|
||||
}
|
||||
cmd_send(CMD_ACK, isOK, pos, 0, 0, 0);
|
||||
LED_C_OFF();
|
||||
BigBuf_free();
|
||||
}
|
||||
|
||||
// unfinished (or not needed?)
|
||||
@ -808,7 +815,6 @@ void SmartCardUpgrade(uint64_t arg0) {
|
||||
void SmartCardSetClock(uint64_t arg0) {
|
||||
LED_D_ON();
|
||||
set_tracing(true);
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
|
||||
// Send SIM CLC
|
||||
|
@ -415,6 +415,8 @@ int CmdHFList(const char *Cmd)
|
||||
protocol = ISO_14443B;
|
||||
} else if(strcmp(type,"topaz") == 0) {
|
||||
protocol = TOPAZ;
|
||||
} else if(strcmp(type, "7816") == 0) {
|
||||
protocol = ISO_7816_4;
|
||||
} else if(strcmp(type,"raw") == 0) {
|
||||
protocol = -1; //No crc, no annotations
|
||||
} else if (strcmp(type, "save") == 0) {
|
||||
@ -592,7 +594,6 @@ static command_t CommandTable[] =
|
||||
{"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"},
|
||||
{"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"},
|
||||
{"epa", CmdHFEPA, 1, "{ German Identification Card... }"},
|
||||
{"emv", CmdHFEMV, 1, "{ EMV cards... }"},
|
||||
{"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"},
|
||||
{"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"},
|
||||
{"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"},
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "util.h"
|
||||
#include "util_posix.h"
|
||||
#include "cmdscript.h"
|
||||
#include "emv/cmdemv.h" // EMV
|
||||
#ifdef WITH_SMARTCARD
|
||||
#include "cmdsmartcard.h"
|
||||
#endif
|
||||
@ -36,18 +37,21 @@ static int CmdQuit(const char *Cmd);
|
||||
|
||||
static command_t CommandTable[] =
|
||||
{
|
||||
{"help", CmdHelp, 1, "This help. Use '<command> help' for details of a particular command."},
|
||||
{"data", CmdData, 1, "{ Plot window / data buffer manipulation... }"},
|
||||
{"hf", CmdHF, 1, "{ High Frequency commands... }"},
|
||||
{"hw", CmdHW, 1, "{ Hardware commands... }"},
|
||||
{"lf", CmdLF, 1, "{ Low Frequency commands... }"},
|
||||
{"help", CmdHelp, 1, "This help. Use '<command> help' for details of a particular command."},
|
||||
{"data", CmdData, 1, "{ Plot window / data buffer manipulation... }"},
|
||||
{"hf", CmdHF, 1, "{ High Frequency commands... }"},
|
||||
{"hw", CmdHW, 1, "{ Hardware commands... }"},
|
||||
{"lf", CmdLF, 1, "{ Low Frequency commands... }"},
|
||||
#ifdef WITH_SMARTCARD
|
||||
{"sc", CmdSmartcard,1,"{ Smartcard commands... }"},
|
||||
{"emv", CmdEMV, 1, "{ EMV iso14443 and iso7816... }"},
|
||||
{"sc", CmdSmartcard,1,"{ Smartcard commands... }"},
|
||||
#else
|
||||
{"emv", CmdEMV, 1, "{ EMV iso14443 }"},
|
||||
#endif
|
||||
{"script",CmdScript,1, "{ Scripting commands }"},
|
||||
{"quit", CmdQuit, 1, "Exit program"},
|
||||
{"exit", CmdQuit, 1, "Exit program"},
|
||||
{NULL, NULL, 0, NULL}
|
||||
{"script",CmdScript,1, "{ Scripting commands }"},
|
||||
{"quit", CmdQuit, 1, "Exit program"},
|
||||
{"exit", CmdQuit, 1, "Exit program"},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
command_t* getTopLevelCommandTable()
|
||||
|
@ -8,66 +8,161 @@
|
||||
// Proxmark3 RDV40 Smartcard module commands
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "cmdsmartcard.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "cmdparser.h"
|
||||
#include "util.h"
|
||||
#include "smartcard.h"
|
||||
#include "comms.h"
|
||||
#include "protocols.h"
|
||||
#include "cmdhf.h" // CmdHFlist
|
||||
#include "emv/apduinfo.h" // APDUcode description
|
||||
#include "emv/emvcore.h" // decodeTVL
|
||||
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
int usage_sm_raw(void) {
|
||||
PrintAndLog("Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" r : do not read response");
|
||||
PrintAndLog(" a : active signal field ON without select");
|
||||
PrintAndLog(" s : active signal field ON with select");
|
||||
PrintAndLog(" t : executes TLV decoder if it is possible");
|
||||
PrintAndLog(" d <bytes> : bytes to send");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc raw d 11223344");
|
||||
static int usage_sm_raw(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>");
|
||||
PrintAndLogEx(NORMAL, " h : this help");
|
||||
PrintAndLogEx(NORMAL, " r : do not read response");
|
||||
PrintAndLogEx(NORMAL, " a : active smartcard without select");
|
||||
PrintAndLogEx(NORMAL, " s : active smartcard with select");
|
||||
PrintAndLogEx(NORMAL, " t : executes TLV decoder if it possible");
|
||||
PrintAndLogEx(NORMAL, " d <bytes> : bytes to send");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " sc raw d 00a404000e315041592e5359532e444446303100 - `1PAY.SYS.DDF01` PPSE directory");
|
||||
PrintAndLogEx(NORMAL, " sc raw d 00a404000e325041592e5359532e444446303100 - `2PAY.SYS.DDF01` PPSE directory");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_reader(void) {
|
||||
PrintAndLog("Usage: sc reader [h|s]");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" s : silent (no messages)");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc reader");
|
||||
|
||||
static int usage_sm_reader(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage: sc reader [h|s]");
|
||||
PrintAndLogEx(NORMAL, " h : this help");
|
||||
PrintAndLogEx(NORMAL, " s : silent (no messages)");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " sc reader");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_info(void) {
|
||||
PrintAndLog("Usage: sc info [h|s]");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" s : silent (no messages)");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc info");
|
||||
|
||||
static int usage_sm_info(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage: s info [h|s]");
|
||||
PrintAndLogEx(NORMAL, " h : this help");
|
||||
PrintAndLogEx(NORMAL, " s : silent (no messages)");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " sc info");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_upgrade(void) {
|
||||
PrintAndLog("Upgrade firmware");
|
||||
PrintAndLog("Usage: sc upgrade f <file name>");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" f <filename> : firmware file name");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc upgrade f myfile");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("WARNING - Dangerous command, do wrong and you will brick the smart card socket");
|
||||
|
||||
static int usage_sm_upgrade(void) {
|
||||
PrintAndLogEx(NORMAL, "Upgrade firmware");
|
||||
PrintAndLogEx(NORMAL, "Usage: sc upgrade f <file name>");
|
||||
PrintAndLogEx(NORMAL, " h : this help");
|
||||
PrintAndLogEx(NORMAL, " f <filename> : firmware file name");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " sc upgrade f myfile");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_setclock(void) {
|
||||
PrintAndLog("Usage: sc setclock [h] c <clockspeed>");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) ");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc setclock c 2");
|
||||
|
||||
static int usage_sm_setclock(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage: sc setclock [h] c <clockspeed>");
|
||||
PrintAndLogEx(NORMAL, " h : this help");
|
||||
PrintAndLogEx(NORMAL, " c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " sc setclock c 2");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usage_sm_brute(void) {
|
||||
PrintAndLogEx(NORMAL, "Tries to bruteforce SFI, ");
|
||||
PrintAndLogEx(NORMAL, "Usage: sc brute [h]");
|
||||
PrintAndLogEx(NORMAL, " h : this help");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " sc brute");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool smart_select(bool silent) {
|
||||
UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}};
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
smart_card_atr_t card;
|
||||
memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t));
|
||||
|
||||
PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int smart_wait(uint8_t *data) {
|
||||
UsbCommand resp;
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
|
||||
PrintAndLogEx(WARNING, "smart card response failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t len = resp.arg[0];
|
||||
if ( !len ) {
|
||||
PrintAndLogEx(WARNING, "smart card response failed");
|
||||
return -2;
|
||||
}
|
||||
memcpy(data, resp.d.asBytes, len);
|
||||
PrintAndLogEx(SUCCESS, " %d | %s", len, sprint_hex_inrow_ex(data, len, 32));
|
||||
|
||||
if (len >= 2) {
|
||||
PrintAndLogEx(SUCCESS, "%02X%02X | %s", data[len - 2], data[len - 1], GetAPDUCodeDescription(data[len - 2], data[len - 1]));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int smart_response(uint8_t *data) {
|
||||
|
||||
int len = -1;
|
||||
int datalen = smart_wait(data);
|
||||
|
||||
if ( data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F ) {
|
||||
len = data[datalen - 1];
|
||||
}
|
||||
|
||||
if (len == -1 ) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Requesting response. len=0x%x", len);
|
||||
uint8_t getstatus[] = {ISO7816_GETSTATUS, 0x00, 0x00, len};
|
||||
UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}};
|
||||
memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) );
|
||||
clearCommandBuffer();
|
||||
SendCommand(&cStatus);
|
||||
|
||||
datalen = smart_wait(data);
|
||||
out:
|
||||
|
||||
return datalen;
|
||||
}
|
||||
|
||||
int CmdSmartRaw(const char *Cmd) {
|
||||
|
||||
int hexlen = 0;
|
||||
@ -99,13 +194,13 @@ int CmdSmartRaw(const char *Cmd) {
|
||||
case 'd': {
|
||||
switch (param_gethex_to_eol(Cmd, cmdp+1, data, sizeof(data), &hexlen)) {
|
||||
case 1:
|
||||
PrintAndLog("Invalid HEX value.");
|
||||
PrintAndLogEx(WARNING, "Invalid HEX value.");
|
||||
return 1;
|
||||
case 2:
|
||||
PrintAndLog("Too many bytes. Max %d bytes", sizeof(data));
|
||||
PrintAndLogEx(WARNING, "Too many bytes. Max %d bytes", sizeof(data));
|
||||
return 1;
|
||||
case 3:
|
||||
PrintAndLog("Hex must have an even number of digits.");
|
||||
PrintAndLogEx(WARNING, "Hex must have even number of digits.");
|
||||
return 1;
|
||||
}
|
||||
cmdp++;
|
||||
@ -113,7 +208,7 @@ int CmdSmartRaw(const char *Cmd) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
@ -130,13 +225,13 @@ int CmdSmartRaw(const char *Cmd) {
|
||||
UsbCommand c = {CMD_SMART_RAW, {0, hexlen, 0}};
|
||||
|
||||
if (active || active_select) {
|
||||
c.arg[0] |= SC_CONNECT;
|
||||
if (active)
|
||||
c.arg[0] |= SC_NO_SELECT;
|
||||
}
|
||||
c.arg[0] |= SC_CONNECT;
|
||||
if (active_select)
|
||||
c.arg[0] |= SC_SELECT;
|
||||
}
|
||||
|
||||
if (hexlen > 0) {
|
||||
c.arg[0] |= SC_RAW;
|
||||
c.arg[0] |= SC_RAW;
|
||||
}
|
||||
|
||||
memcpy(c.d.asBytes, data, hexlen );
|
||||
@ -145,45 +240,67 @@ int CmdSmartRaw(const char *Cmd) {
|
||||
|
||||
// reading response from smart card
|
||||
if ( reply ) {
|
||||
UsbCommand resp;
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
|
||||
PrintAndLog("smart card response failed");
|
||||
return 1;
|
||||
}
|
||||
uint32_t datalen = resp.arg[0];
|
||||
|
||||
if ( !datalen ) {
|
||||
PrintAndLog("smart card response failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PrintAndLog("received %i bytes", datalen);
|
||||
|
||||
if (!datalen)
|
||||
uint8_t* buf = calloc(USB_CMD_DATA_SIZE, sizeof(uint8_t));
|
||||
if ( !buf )
|
||||
return 1;
|
||||
|
||||
uint8_t *data = resp.d.asBytes;
|
||||
|
||||
// TLV decoder
|
||||
if (decodeTLV ) {
|
||||
|
||||
if (datalen >= 2) {
|
||||
PrintAndLog("%02x %02x | %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1]));
|
||||
}
|
||||
if (datalen > 4) {
|
||||
TLVPrintFromBuffer(data, datalen - 2);
|
||||
}
|
||||
} else {
|
||||
PrintAndLog("%s", sprint_hex(data, datalen));
|
||||
int len = smart_response(buf);
|
||||
if ( len < 0 ) {
|
||||
free(buf);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ( buf[0] == 0x6C ) {
|
||||
data[4] = buf[1];
|
||||
|
||||
memcpy(c.d.asBytes, data, sizeof(data) );
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
len = smart_response(buf);
|
||||
|
||||
data[4] = 0;
|
||||
}
|
||||
|
||||
if (decodeTLV && len > 4)
|
||||
TLVPrintFromBuffer(buf+1, len-3);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
|
||||
*dataoutlen = 0;
|
||||
|
||||
if (activateCard)
|
||||
smart_select(false);
|
||||
printf("* APDU SC\n");
|
||||
|
||||
UsbCommand c = {CMD_SMART_RAW, {SC_RAW | SC_CONNECT, datainlen, 0}};
|
||||
if (activateCard) {
|
||||
c.arg[0] |= SC_SELECT;
|
||||
}
|
||||
memcpy(c.d.asBytes, datain, datainlen);
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
|
||||
int len = smart_response(dataout);
|
||||
|
||||
if ( len < 0 ) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
*dataoutlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int CmdSmartUpgrade(const char *Cmd) {
|
||||
|
||||
PrintAndLog("WARNING - Smartcard socket firmware upgrade.");
|
||||
PrintAndLog("Dangerous command, do wrong and you will brick the smart card socket");
|
||||
PrintAndLogEx(WARNING, "WARNING - Smartcard socket firmware upgrade.");
|
||||
PrintAndLogEx(WARNING, "A dangerous command, do wrong and you will brick the smart card socket");
|
||||
|
||||
FILE *f;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
@ -195,7 +312,7 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
case 'f':
|
||||
//File handling and reading
|
||||
if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) {
|
||||
PrintAndLog("Filename too long");
|
||||
PrintAndLogEx(FAILED, "Filename too long");
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
@ -204,7 +321,7 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
case 'h':
|
||||
return usage_sm_upgrade();
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
@ -215,8 +332,8 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
|
||||
// load file
|
||||
f = fopen(filename, "rb");
|
||||
if ( !f ) {
|
||||
PrintAndLog("File: %s: not found or locked.", filename);
|
||||
if ( !f ){
|
||||
PrintAndLogEx(FAILED, "File: %s: not found or locked.", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -225,15 +342,15 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (fsize < 0) {
|
||||
PrintAndLog("error, when getting filesize");
|
||||
if (fsize < 0) {
|
||||
PrintAndLogEx(WARNING, "error, when getting filesize");
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint8_t *dump = calloc(fsize, sizeof(uint8_t));
|
||||
if (!dump) {
|
||||
PrintAndLog("error, cannot allocate memory ");
|
||||
PrintAndLogEx(WARNING, "error, cannot allocate memory ");
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
@ -242,7 +359,7 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
PrintAndLog("Smartcard socket firmware uploading to PM3");
|
||||
PrintAndLogEx(SUCCESS, "Smartcard socket firmware uploading to PM3");
|
||||
//Send to device
|
||||
uint32_t index = 0;
|
||||
uint32_t bytes_sent = 0;
|
||||
@ -258,7 +375,7 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2000) ) {
|
||||
PrintAndLog("timeout while waiting for reply.");
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
free(dump);
|
||||
return 1;
|
||||
}
|
||||
@ -269,7 +386,7 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
}
|
||||
free(dump);
|
||||
printf("\n");
|
||||
PrintAndLog("Smartcard socket firmware updating, don\'t turn off your PM3!");
|
||||
PrintAndLogEx(SUCCESS, "Smartcard socket firmware updating, don\'t turn off your PM3!");
|
||||
|
||||
// trigger the firmware upgrade
|
||||
UsbCommand c = {CMD_SMART_UPGRADE, {bytes_read, 0, 0}};
|
||||
@ -277,13 +394,13 @@ int CmdSmartUpgrade(const char *Cmd) {
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
PrintAndLog("timeout while waiting for reply.");
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
return 1;
|
||||
}
|
||||
if ( (resp.arg[0] && 0xFF ) )
|
||||
PrintAndLog("Smartcard socket firmware upgraded successful");
|
||||
if ( (resp.arg[0] & 0xFF ) )
|
||||
PrintAndLogEx(SUCCESS, "Smartcard socket firmware upgraded successful");
|
||||
else
|
||||
PrintAndLog("Smartcard socket firmware updating failed");
|
||||
PrintAndLogEx(FAILED, "Smartcard socket firmware updating failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -294,11 +411,11 @@ int CmdSmartInfo(const char *Cmd){
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_info();
|
||||
case 's':
|
||||
case 's':
|
||||
silent = true;
|
||||
break;
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
@ -313,13 +430,13 @@ int CmdSmartInfo(const char *Cmd){
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -327,11 +444,11 @@ int CmdSmartInfo(const char *Cmd){
|
||||
memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t));
|
||||
|
||||
// print header
|
||||
PrintAndLog("\n--- Smartcard Information ---------");
|
||||
PrintAndLog("-------------------------------------------------------------");
|
||||
PrintAndLog("ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
PrintAndLog("look up ATR");
|
||||
PrintAndLog("http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) );
|
||||
PrintAndLogEx(INFO, "\n--- Smartcard Information ---------");
|
||||
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
||||
PrintAndLogEx(INFO, "ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
PrintAndLogEx(INFO, "look up ATR");
|
||||
PrintAndLogEx(INFO, "http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -342,11 +459,11 @@ int CmdSmartReader(const char *Cmd){
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_reader();
|
||||
case 's':
|
||||
case 's':
|
||||
silent = true;
|
||||
break;
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
@ -361,18 +478,19 @@ int CmdSmartReader(const char *Cmd){
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
smart_card_atr_t card;
|
||||
memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t));
|
||||
PrintAndLog("ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
|
||||
PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -383,15 +501,15 @@ int CmdSmartSetClock(const char *Cmd){
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_setclock();
|
||||
case 'c':
|
||||
case 'c':
|
||||
clock = param_get8ex(Cmd, cmdp+1, 2, 10);
|
||||
if ( clock > 2)
|
||||
errors = true;
|
||||
|
||||
|
||||
cmdp += 2;
|
||||
break;
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
@ -405,25 +523,25 @@ int CmdSmartSetClock(const char *Cmd){
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
PrintAndLog("smart card select failed");
|
||||
PrintAndLogEx(WARNING, "smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
PrintAndLog("smart card set clock failed");
|
||||
PrintAndLogEx(WARNING, "smart card set clock failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (clock) {
|
||||
case 0:
|
||||
PrintAndLog("Clock changed to 16mhz giving 10800 baudrate");
|
||||
PrintAndLogEx(SUCCESS, "Clock changed to 16mhz giving 10800 baudrate");
|
||||
break;
|
||||
case 1:
|
||||
PrintAndLog("Clock changed to 8mhz giving 21600 baudrate");
|
||||
PrintAndLogEx(SUCCESS, "Clock changed to 8mhz giving 21600 baudrate");
|
||||
break;
|
||||
case 2:
|
||||
PrintAndLog("Clock changed to 4mhz giving 86400 baudrate");
|
||||
PrintAndLogEx(SUCCESS, "Clock changed to 4mhz giving 86400 baudrate");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -431,267 +549,77 @@ int CmdSmartSetClock(const char *Cmd){
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// iso 7816-3
|
||||
void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){
|
||||
// S-block
|
||||
if ( (cmd[0] & 0xC0) && (cmdsize == 3) ) {
|
||||
switch ( (cmd[0] & 0x3f) ) {
|
||||
case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break;
|
||||
case 0x20 : snprintf(exp, size, "S-block RESYNCH resp"); break;
|
||||
case 0x01 : snprintf(exp, size, "S-block IFS req"); break;
|
||||
case 0x21 : snprintf(exp, size, "S-block IFS resp"); break;
|
||||
case 0x02 : snprintf(exp, size, "S-block ABORT req"); break;
|
||||
case 0x22 : snprintf(exp, size, "S-block ABORT resp"); break;
|
||||
case 0x03 : snprintf(exp, size, "S-block WTX reqt"); break;
|
||||
case 0x23 : snprintf(exp, size, "S-block WTX resp"); break;
|
||||
default : snprintf(exp, size, "S-block"); break;
|
||||
}
|
||||
}
|
||||
// R-block (ack)
|
||||
else if ( ((cmd[0] & 0xD0) == 0x80) && ( cmdsize > 2) ) {
|
||||
if ( (cmd[0] & 0x10) == 0 )
|
||||
snprintf(exp, size, "R-block ACK");
|
||||
else
|
||||
snprintf(exp, size, "R-block NACK");
|
||||
}
|
||||
// I-block
|
||||
else {
|
||||
|
||||
int pos = (cmd[0] == 2 || cmd[0] == 3) ? 2 : 3;
|
||||
switch ( cmd[pos] ) {
|
||||
case ISO7816_READ_BINARY :snprintf(exp, size, "READ BIN");break;
|
||||
case ISO7816_WRITE_BINARY :snprintf(exp, size, "WRITE BIN");break;
|
||||
case ISO7816_UPDATE_BINARY :snprintf(exp, size, "UPDATE BIN");break;
|
||||
case ISO7816_ERASE_BINARY :snprintf(exp, size, "ERASE BIN");break;
|
||||
case ISO7816_READ_RECORDS :snprintf(exp, size, "READ RECORDS");break;
|
||||
case ISO7816_WRITE_RECORDS :snprintf(exp, size, "WRITE RECORDS");break;
|
||||
case ISO7816_APPEND_RECORD :snprintf(exp, size, "APPEND RECORD");break;
|
||||
case ISO7816_UPDATE_RECORD :snprintf(exp, size, "UPDATE RECORD");break;
|
||||
case ISO7816_GET_DATA :snprintf(exp, size, "GET DATA");break;
|
||||
case ISO7816_PUT_DATA :snprintf(exp, size, "PUT DATA");break;
|
||||
case ISO7816_SELECT_FILE :snprintf(exp, size, "SELECT FILE");break;
|
||||
case ISO7816_VERIFY :snprintf(exp, size, "VERIFY");break;
|
||||
case ISO7816_INTERNAL_AUTHENTICATION :snprintf(exp, size, "INTERNAL AUTH");break;
|
||||
case ISO7816_EXTERNAL_AUTHENTICATION :snprintf(exp, size, "EXTERNAL AUTH");break;
|
||||
case ISO7816_GET_CHALLENGE :snprintf(exp, size, "GET CHALLENGE");break;
|
||||
case ISO7816_MANAGE_CHANNEL :snprintf(exp, size, "MANAGE CHANNEL");break;
|
||||
default :snprintf(exp, size, "?"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint16_t printScTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) {
|
||||
// sanity check
|
||||
if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen;
|
||||
|
||||
bool isResponse;
|
||||
uint16_t data_len, parity_len;
|
||||
uint32_t duration, timestamp, first_timestamp, EndOfTransmissionTimestamp;
|
||||
char explanation[30] = {0};
|
||||
|
||||
first_timestamp = *((uint32_t *)(trace));
|
||||
timestamp = *((uint32_t *)(trace + tracepos));
|
||||
tracepos += 4;
|
||||
|
||||
duration = *((uint16_t *)(trace + tracepos));
|
||||
tracepos += 2;
|
||||
|
||||
data_len = *((uint16_t *)(trace + tracepos));
|
||||
tracepos += 2;
|
||||
|
||||
if (data_len & 0x8000) {
|
||||
data_len &= 0x7fff;
|
||||
isResponse = true;
|
||||
} else {
|
||||
isResponse = false;
|
||||
}
|
||||
|
||||
parity_len = (data_len-1)/8 + 1;
|
||||
if (tracepos + data_len + parity_len > traceLen) {
|
||||
return traceLen;
|
||||
}
|
||||
uint8_t *frame = trace + tracepos;
|
||||
tracepos += data_len;
|
||||
//uint8_t *parityBytes = trace + tracepos;
|
||||
tracepos += parity_len;
|
||||
|
||||
//--- Draw the data column
|
||||
char line[18][110];
|
||||
|
||||
if (data_len == 0 ) {
|
||||
sprintf(line[0],"<empty trace - possible error>");
|
||||
return tracepos;
|
||||
}
|
||||
|
||||
for (int j = 0; j < data_len && j/18 < 18; j++) {
|
||||
snprintf(line[j/18]+(( j % 18) * 4),110, "%02x ", frame[j]);
|
||||
}
|
||||
|
||||
EndOfTransmissionTimestamp = timestamp + duration;
|
||||
|
||||
annotateIso7816(explanation,sizeof(explanation),frame,data_len);
|
||||
|
||||
int num_lines = MIN((data_len - 1)/18 + 1, 18);
|
||||
for (int j = 0; j < num_lines ; j++) {
|
||||
if (j == 0) {
|
||||
PrintAndLog(" %10u | %10u | %s |%-72s | %s| %s",
|
||||
(timestamp - first_timestamp),
|
||||
(EndOfTransmissionTimestamp - first_timestamp),
|
||||
(isResponse ? "Tag" : "Rdr"),
|
||||
line[j],
|
||||
" ",
|
||||
(j == num_lines-1) ? explanation : "");
|
||||
} else {
|
||||
PrintAndLog(" | | |%-72s | %s| %s",
|
||||
line[j],
|
||||
" ",
|
||||
(j == num_lines-1) ? explanation : "");
|
||||
}
|
||||
}
|
||||
|
||||
// if is last record
|
||||
if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen) return traceLen;
|
||||
|
||||
return tracepos;
|
||||
}
|
||||
|
||||
int ScTraceList(const char *Cmd) {
|
||||
bool loadFromFile = false;
|
||||
bool saveToFile = false;
|
||||
char type[5] = {0};
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
|
||||
// parse command line
|
||||
param_getstr(Cmd, 0, type, sizeof(type));
|
||||
param_getstr(Cmd, 1, filename, sizeof(filename));
|
||||
|
||||
bool errors = false;
|
||||
if(type[0] == 'h') {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
if(!errors) {
|
||||
if (strcmp(type, "s") == 0) {
|
||||
saveToFile = true;
|
||||
} else if (strcmp(type,"l") == 0) {
|
||||
loadFromFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((loadFromFile || saveToFile) && strlen(filename) == 0) {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
if (loadFromFile && saveToFile) {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
PrintAndLog("List or save protocol data.");
|
||||
PrintAndLog("Usage: sc list [l <filename>]");
|
||||
PrintAndLog(" sc list [s <filename>]");
|
||||
PrintAndLog(" l - load data from file instead of trace buffer");
|
||||
PrintAndLog(" s - save data to file");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("example: sc list");
|
||||
PrintAndLog("example: sc list save myCardTrace.trc");
|
||||
PrintAndLog("example: sc list l myCardTrace.trc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *trace;
|
||||
uint32_t tracepos = 0;
|
||||
uint32_t traceLen = 0;
|
||||
|
||||
if (loadFromFile) {
|
||||
#define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions
|
||||
FILE *tracefile = NULL;
|
||||
size_t bytes_read;
|
||||
trace = malloc(TRACE_CHUNK_SIZE);
|
||||
if (trace == NULL) {
|
||||
PrintAndLog("Cannot allocate memory for trace");
|
||||
return 2;
|
||||
}
|
||||
if ((tracefile = fopen(filename,"rb")) == NULL) {
|
||||
PrintAndLog("Could not open file %s", filename);
|
||||
free(trace);
|
||||
return 0;
|
||||
}
|
||||
while (!feof(tracefile)) {
|
||||
bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile);
|
||||
traceLen += bytes_read;
|
||||
if (!feof(tracefile)) {
|
||||
uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE);
|
||||
if (p == NULL) {
|
||||
PrintAndLog("Cannot allocate memory for trace");
|
||||
free(trace);
|
||||
fclose(tracefile);
|
||||
return 2;
|
||||
}
|
||||
trace = p;
|
||||
}
|
||||
}
|
||||
fclose(tracefile);
|
||||
} else {
|
||||
trace = malloc(USB_CMD_DATA_SIZE);
|
||||
// Query for the size of the trace
|
||||
UsbCommand response;
|
||||
GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false);
|
||||
traceLen = response.arg[2];
|
||||
if (traceLen > USB_CMD_DATA_SIZE) {
|
||||
uint8_t *p = realloc(trace, traceLen);
|
||||
if (p == NULL) {
|
||||
PrintAndLog("Cannot allocate memory for trace");
|
||||
free(trace);
|
||||
return 2;
|
||||
}
|
||||
trace = p;
|
||||
GetFromBigBuf(trace, traceLen, 0, NULL, -1, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (saveToFile) {
|
||||
FILE *tracefile = NULL;
|
||||
if ((tracefile = fopen(filename,"wb")) == NULL) {
|
||||
PrintAndLog("Could not create file %s", filename);
|
||||
return 1;
|
||||
}
|
||||
fwrite(trace, 1, traceLen, tracefile);
|
||||
PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename);
|
||||
fclose(tracefile);
|
||||
} else {
|
||||
PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen);
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer");
|
||||
PrintAndLog("");
|
||||
PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |");
|
||||
PrintAndLog("------------|------------|-----|-------------------------------------------------------------------------|-----|--------------------|");
|
||||
|
||||
while(tracepos < traceLen)
|
||||
{
|
||||
tracepos = printScTraceLine(tracepos, traceLen, trace);
|
||||
}
|
||||
}
|
||||
|
||||
free(trace);
|
||||
int CmdSmartList(const char *Cmd) {
|
||||
CmdHFList("7816");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartList(const char *Cmd) {
|
||||
ScTraceList(Cmd);
|
||||
int CmdSmartBruteforceSFI(const char *Cmd) {
|
||||
|
||||
char ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (ctmp == 'h') return usage_sm_brute();
|
||||
|
||||
uint8_t data[5] = {0x00, 0xB2, 0x00, 0x00, 0x00};
|
||||
|
||||
PrintAndLogEx(INFO, "Selecting card");
|
||||
if ( !smart_select(false) ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Selecting PPSE aid");
|
||||
CmdSmartRaw("d 00a404000e325041592e5359532e444446303100");
|
||||
CmdSmartRaw("d 00a4040007a000000004101000");
|
||||
|
||||
PrintAndLogEx(INFO, "starting");
|
||||
|
||||
UsbCommand c = {CMD_SMART_RAW, {SC_RAW, sizeof(data), 0}};
|
||||
uint8_t* buf = malloc(USB_CMD_DATA_SIZE);
|
||||
if ( !buf )
|
||||
return 1;
|
||||
|
||||
for (uint8_t i=1; i < 4; i++) {
|
||||
for (int p1=1; p1 < 5; p1++) {
|
||||
|
||||
data[2] = p1;
|
||||
data[3] = (i << 3) + 4;
|
||||
|
||||
memcpy(c.d.asBytes, data, sizeof(data) );
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
|
||||
smart_response(buf);
|
||||
|
||||
// if 0x6C
|
||||
if ( buf[0] == 0x6C ) {
|
||||
data[4] = buf[1];
|
||||
|
||||
memcpy(c.d.asBytes, data, sizeof(data) );
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
uint8_t len = smart_response(buf);
|
||||
|
||||
// TLV decoder
|
||||
if (len > 4)
|
||||
TLVPrintFromBuffer(buf+1, len-3);
|
||||
|
||||
data[4] = 0;
|
||||
}
|
||||
memset(buf, 0x00, USB_CMD_DATA_SIZE);
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, 1, "This help"},
|
||||
{"list", CmdSmartList, 0, "List ISO 7816 history"},
|
||||
{"info", CmdSmartInfo, 1, "Tag information [rdv40]"},
|
||||
{"reader", CmdSmartReader, 1, "Act like an IS07816 reader [rdv40]"},
|
||||
{"raw", CmdSmartRaw, 1, "Send raw hex data to tag [rdv40]"},
|
||||
{"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware [rdv40]"},
|
||||
{"setclock",CmdSmartSetClock, 1, "Set clock speed"},
|
||||
{"help", CmdHelp, 1, "This help"},
|
||||
{"list", CmdSmartList, 0, "List ISO 7816 history"},
|
||||
{"info", CmdSmartInfo, 1, "Tag information"},
|
||||
{"reader", CmdSmartReader, 1, "Act like an IS07816 reader"},
|
||||
{"raw", CmdSmartRaw, 1, "Send raw hex data to tag"},
|
||||
{"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware"},
|
||||
{"setclock", CmdSmartSetClock, 1, "Set clock speed"},
|
||||
{"brute", CmdSmartBruteforceSFI, 1, "Bruteforce SFI"},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
@ -11,19 +11,8 @@
|
||||
#ifndef CMDSMARTCARD_H__
|
||||
#define CMDSMARTCARD_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "proxmark3.h"
|
||||
#include "ui.h"
|
||||
#include "cmdparser.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
#include "loclass/fileutils.h" // saveFile
|
||||
#include "cmdmain.h" // getfromdevice
|
||||
#include "emv/emvcore.h" // decodeTVL
|
||||
#include "emv/apduinfo.h" // APDUcode description
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern int CmdSmartcard(const char *Cmd);
|
||||
|
||||
@ -32,8 +21,6 @@ extern int CmdSmartUpgrade(const char* cmd);
|
||||
extern int CmdSmartInfo(const char* cmd);
|
||||
extern int CmdSmartReader(const char *Cmd);
|
||||
|
||||
extern int usage_sm_raw(void);
|
||||
extern int usage_sm_reader(void);
|
||||
extern int usage_sm_info(void);
|
||||
extern int usage_sm_upgrade(void);
|
||||
extern int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,13 @@
|
||||
#include "emvcore.h"
|
||||
#include "apduinfo.h"
|
||||
|
||||
int CmdHFEMV(const char *Cmd);
|
||||
int CmdEMV(const char *Cmd);
|
||||
|
||||
extern int CmdEMVSelect(const char *cmd);
|
||||
extern int CmdEMVSearch(const char *cmd);
|
||||
extern int CmdEMVPPSE(const char *cmd);
|
||||
extern int CmdEMVExec(const char *cmd);
|
||||
extern int CmdEMVGetrng(const char *Cmd);
|
||||
extern int CmdEMVList(const char *Cmd);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "emvcore.h"
|
||||
#include "emvjson.h"
|
||||
#include "util_posix.h"
|
||||
#ifdef WITH_SMARTCARD
|
||||
#include "cmdsmartcard.h"
|
||||
#endif
|
||||
|
||||
// Got from here. Thanks)
|
||||
// https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix
|
||||
@ -230,12 +233,13 @@ struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) {
|
||||
return tlvdb_fixed(0x02, dCVVlen, dCVV);
|
||||
}
|
||||
|
||||
int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
int EMVExchangeEx(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
uint8_t data[APDU_RES_LEN] = {0};
|
||||
|
||||
*ResultLen = 0;
|
||||
if (sw) *sw = 0;
|
||||
uint16_t isw = 0;
|
||||
int res = 0;
|
||||
|
||||
if (ActivateField){
|
||||
DropField();
|
||||
@ -250,16 +254,32 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
|
||||
if (APDULogging)
|
||||
PrintAndLog(">>>> %s", sprint_hex(data, (IncludeLe?6:5) + apdu.Lc));
|
||||
|
||||
// 6 byes + data = INS + CLA + P1 + P2 + Lc + <data = Nc> + Le(?IncludeLe)
|
||||
int res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
switch(channel) {
|
||||
case ECC_CONTACTLESS:
|
||||
// 6 byes + data = INS + CLA + P1 + P2 + Lc + <data = Nc> + Le(?IncludeLe)
|
||||
res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
case ECC_CONTACT:
|
||||
//int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
|
||||
#ifdef WITH_SMARTCARD
|
||||
res = ExchangeAPDUSC(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (APDULogging)
|
||||
PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen));
|
||||
|
||||
if (*ResultLen < 2) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
*ResultLen -= 2;
|
||||
isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
|
||||
if (sw)
|
||||
@ -285,15 +305,15 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchangeEx(false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchangeEx(channel, false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
|
||||
int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchangeEx(channel, ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
|
||||
int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
uint8_t buf[APDU_AID_LEN] = {0};
|
||||
*ResultLen = 0;
|
||||
int len = 0;
|
||||
@ -310,19 +330,19 @@ int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t
|
||||
}
|
||||
|
||||
// select
|
||||
res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL);
|
||||
res = EMVSelect(channel, ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
|
||||
int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
|
||||
uint8_t data[APDU_RES_LEN] = {0};
|
||||
size_t datalen = 0;
|
||||
uint16_t sw = 0;
|
||||
int res;
|
||||
|
||||
// select PPSE
|
||||
res = EMVSelectPSE(ActivateField, true, 2, data, sizeof(data), &datalen, &sw);
|
||||
res = EMVSelectPSE(channel, ActivateField, true, 2, data, sizeof(data), &datalen, &sw);
|
||||
|
||||
if (!res){
|
||||
struct tlvdb *t = NULL;
|
||||
@ -336,7 +356,7 @@ int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct t
|
||||
while (ttmp) {
|
||||
const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL);
|
||||
if (tgAID) {
|
||||
res = EMVSelect(false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv);
|
||||
res = EMVSelect(channel, false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv);
|
||||
|
||||
// retry if error and not returned sw error
|
||||
if (res && res != 5) {
|
||||
@ -383,7 +403,7 @@ int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct t
|
||||
return res;
|
||||
}
|
||||
|
||||
int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
|
||||
int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
|
||||
uint8_t aidbuf[APDU_AID_LEN] = {0};
|
||||
int aidlen = 0;
|
||||
uint8_t data[APDU_RES_LEN] = {0};
|
||||
@ -394,15 +414,15 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd
|
||||
int retrycnt = 0;
|
||||
for(int i = 0; i < AIDlistLen; i ++) {
|
||||
param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen);
|
||||
res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv);
|
||||
res = EMVSelect(channel, (i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv);
|
||||
// retry if error and not returned sw error
|
||||
if (res && res != 5) {
|
||||
if (++retrycnt < 3){
|
||||
i--;
|
||||
} else {
|
||||
// card select error, proxmark error
|
||||
if (res == 1) {
|
||||
PrintAndLog("Exit...");
|
||||
// (1) - card select error, proxmark error OR (200) - result length = 0
|
||||
if (res == 1 || res == 200) {
|
||||
PrintAndLogEx(WARNING, "Exit...");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -464,38 +484,38 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
|
||||
int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
int res = EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
if (*sw == 0x6700) {
|
||||
PrintAndLog(">>> trying to reissue command withouth Le...");
|
||||
res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
|
||||
res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
|
||||
int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
int res = EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
if (*sw == 0x6700) {
|
||||
PrintAndLog(">>> trying to reissue command withouth Le...");
|
||||
res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
|
||||
res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
|
||||
int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
|
||||
return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
|
||||
}
|
||||
|
||||
// Authentication
|
||||
@ -565,7 +585,7 @@ int trSDA(struct tlvdb *tlv) {
|
||||
static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04};
|
||||
static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value };
|
||||
|
||||
int trDDA(bool decodeTLV, struct tlvdb *tlv) {
|
||||
int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) {
|
||||
uint8_t buf[APDU_RES_LEN] = {0};
|
||||
size_t len = 0;
|
||||
uint16_t sw = 0;
|
||||
@ -705,9 +725,9 @@ int trDDA(bool decodeTLV, struct tlvdb *tlv) {
|
||||
PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len));
|
||||
|
||||
PrintAndLog("\n* Internal Authenticate");
|
||||
int res = EMVInternalAuthenticate(true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL);
|
||||
int res = EMVInternalAuthenticate(channel, true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL);
|
||||
if (res) {
|
||||
PrintAndLog("Internal Authenticate error(%d): %4x. Exit...", res, sw);
|
||||
PrintAndLogEx(WARNING, "Internal Authenticate error(%d): %4x. Exit...", res, sw);
|
||||
free(ddol_data_tlv);
|
||||
emv_pk_free(pk);
|
||||
emv_pk_free(issuer_pk);
|
||||
|
@ -32,6 +32,11 @@
|
||||
#define APDU_RES_LEN 260
|
||||
#define APDU_AID_LEN 50
|
||||
|
||||
typedef enum {
|
||||
ECC_CONTACTLESS,
|
||||
ECC_CONTACT
|
||||
} EMVCommandChannel;
|
||||
|
||||
enum TransactionType {
|
||||
TT_MSD,
|
||||
TT_VSDC, // not standart for contactless!!!!
|
||||
@ -71,29 +76,29 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
|
||||
extern void SetAPDULogging(bool logging);
|
||||
|
||||
// exchange
|
||||
extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
|
||||
|
||||
// search application
|
||||
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||
extern int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||
extern int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||
extern int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// select application
|
||||
extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen);
|
||||
// Get Processing Options
|
||||
extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// AC
|
||||
extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// DDA
|
||||
extern int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
extern int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// Mastercard
|
||||
int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// Auth
|
||||
extern int trSDA(struct tlvdb *tlv);
|
||||
extern int trDDA(bool decodeTLV, struct tlvdb *tlv);
|
||||
extern int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv);
|
||||
extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv);
|
||||
|
||||
extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root);
|
||||
|
@ -170,17 +170,17 @@ char *fido2GetCmdMemberDescription(uint8_t cmdCode, bool isResponse, int memberN
|
||||
int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
|
||||
|
||||
return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
|
||||
return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
|
||||
}
|
||||
|
||||
int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
|
||||
int res = EMVExchange(ECC_CONTACTLESS, true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
|
||||
if (res == 5) // apdu result (sw) not a 0x9000
|
||||
res = 0;
|
||||
// software chaining
|
||||
while (!res && (*sw >> 8) == 0x61) {
|
||||
size_t oldlen = *ResultLen;
|
||||
res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
|
||||
res = EMVExchange(ECC_CONTACTLESS, true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
|
||||
if (res == 5) // apdu result (sw) not a 0x9000
|
||||
res = 0;
|
||||
|
||||
|
@ -13,6 +13,7 @@ APP_CFLAGS += -DWITH_ISO14443a_StandAlone \
|
||||
-DWITH_HITAG \
|
||||
-DWITH_CRC \
|
||||
-DWITH_HFSNOOP \
|
||||
-DWITH_SMARTCARD \
|
||||
-DWITH_GUI
|
||||
#END
|
||||
|
||||
|
@ -262,6 +262,7 @@ NXP/Philips CUSTOM COMMANDS
|
||||
#define ISO7816_EXTERNAL_AUTHENTICATION 0x82
|
||||
#define ISO7816_GET_CHALLENGE 0xB4
|
||||
#define ISO7816_MANAGE_CHANNEL 0x70
|
||||
#define ISO7816_GETSTATUS 0xC0
|
||||
// ISO7816-4 For response APDU's
|
||||
#define ISO7816_OK 0x9000
|
||||
// 6x xx = ERROR
|
||||
|
@ -22,7 +22,7 @@ typedef enum SMARTCARD_COMMAND {
|
||||
SC_CONNECT = (1 << 0),
|
||||
SC_NO_DISCONNECT = (1 << 1),
|
||||
SC_RAW = (1 << 2),
|
||||
SC_NO_SELECT = (1 << 3)
|
||||
SC_SELECT = (1 << 3)
|
||||
} smartcard_command_t;
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user