CHG: 'sc brute' - now is interruptable, optional tlv decode,

This commit is contained in:
iceman1001 2018-12-23 09:48:46 +01:00
commit acb41a7376
7 changed files with 291 additions and 91 deletions

1
client/aidlist.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -914,7 +914,7 @@ static command_t CommandTable[] =
}; };
int CmdHFFido(const char *Cmd) { int CmdHFFido(const char *Cmd) {
(void)WaitForResponseTimeout(CMD_ACK,NULL,100); (void)WaitForResponseTimeout(CMD_ACK, NULL, 100);
CmdsParse(CommandTable, Cmd); CmdsParse(CommandTable, Cmd);
return 0; return 0;
} }

View file

@ -8,6 +8,7 @@
// Proxmark3 RDV40 Smartcard module commands // Proxmark3 RDV40 Smartcard module commands
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "cmdsmartcard.h" #include "cmdsmartcard.h"
#include "../emv/emvjson.h"
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
@ -24,6 +25,9 @@ int usage_sm_raw(void) {
PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " sc raw s 0 d 00a404000e315041592e5359532e4444463031 - `1PAY.SYS.DDF01` PPSE directory with get ATR"); PrintAndLogEx(NORMAL, " sc raw s 0 d 00a404000e315041592e5359532e4444463031 - `1PAY.SYS.DDF01` PPSE directory with get ATR");
PrintAndLogEx(NORMAL, " sc raw 0 d 00a404000e325041592e5359532e4444463031 - `2PAY.SYS.DDF01` PPSE directory"); PrintAndLogEx(NORMAL, " sc raw 0 d 00a404000e325041592e5359532e4444463031 - `2PAY.SYS.DDF01` PPSE directory");
PrintAndLogEx(NORMAL, " sc raw 0 t d 00a4040007a000000004101000 - Mastercard");
PrintAndLogEx(NORMAL, " sc raw 0 t d 00a4040007a0000000031010 - Visa");
return 0; return 0;
} }
int usage_sm_reader(void) { int usage_sm_reader(void) {
@ -64,15 +68,48 @@ int usage_sm_setclock(void) {
return 0; return 0;
} }
int usage_sm_brute(void) { int usage_sm_brute(void) {
PrintAndLogEx(NORMAL, "Tries to bruteforce SFI, "); PrintAndLogEx(NORMAL, "Tries to bruteforce SFI, using a known list of AID's ");
PrintAndLogEx(NORMAL, "Usage: sc brute [h]"); PrintAndLogEx(NORMAL, "Usage: sc brute [h]");
PrintAndLogEx(NORMAL, " h : this help"); PrintAndLogEx(NORMAL, " h : this help");
PrintAndLogEx(NORMAL, " t : executes TLV decoder if it possible");
// PrintAndLogEx(NORMAL, " 0 : use protocol T=0");
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " sc brute"); PrintAndLogEx(NORMAL, " sc brute t");
return 0; return 0;
} }
static int smart_loadjson(const char *preferredName, const char *suffix, json_t **root) {
json_error_t error;
if ( preferredName == NULL ) return 1;
if ( suffix == NULL ) return 1;
int retval = 0;
int size = sizeof(char) * (strlen(preferredName) + strlen(suffix) + 10);
char * fileName = calloc(size, sizeof(char));
sprintf(fileName, "%s.%s", preferredName, suffix);
*root = json_load_file(fileName, 0, &error);
if (!*root) {
PrintAndLogEx(ERR, "json (%s) error on line %d: %s", fileName, error.line, error.text);
retval = 2;
goto out;
}
if (!json_is_array(*root)) {
PrintAndLogEx(ERR, "Invalid json (%s) format. root must be an array.", fileName);
retval = 3;
goto out;
}
PrintAndLogEx(SUCCESS, "Loaded file (%s) OK.", fileName);
out:
free(fileName);
return retval;
}
uint8_t GetATRTA1(uint8_t *atr, size_t atrlen) { uint8_t GetATRTA1(uint8_t *atr, size_t atrlen) {
if (atrlen > 2) { if (atrlen > 2) {
uint8_t T0 = atr[1]; uint8_t T0 = atr[1];
@ -142,19 +179,16 @@ float FArray[] = {
int GetATRDi(uint8_t *atr, size_t atrlen) { int GetATRDi(uint8_t *atr, size_t atrlen) {
uint8_t TA1 = GetATRTA1(atr, atrlen); uint8_t TA1 = GetATRTA1(atr, atrlen);
return DiArray[TA1 & 0x0F]; // The 4 low-order bits of TA1 (4th MSbit to 1st LSbit) encode Di
return DiArray[TA1 & 0x0f]; // The 4 low-order bits of TA1 (4th MSbit to 1st LSbit) encode Di
} }
int GetATRFi(uint8_t *atr, size_t atrlen) { int GetATRFi(uint8_t *atr, size_t atrlen) {
uint8_t TA1 = GetATRTA1(atr, atrlen); uint8_t TA1 = GetATRTA1(atr, atrlen);
return FiArray[TA1 >> 4]; // The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi return FiArray[TA1 >> 4]; // The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi
} }
float GetATRF(uint8_t *atr, size_t atrlen) { float GetATRF(uint8_t *atr, size_t atrlen) {
uint8_t TA1 = GetATRTA1(atr, atrlen); uint8_t TA1 = GetATRTA1(atr, atrlen);
return FArray[TA1 >> 4]; // The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi return FArray[TA1 >> 4]; // The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi
} }
@ -284,31 +318,31 @@ static bool smart_select(bool silent) {
return true; return true;
} }
static int smart_wait(uint8_t *data) { static int smart_wait(uint8_t *data, bool silent) {
UsbCommand resp; UsbCommand resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
PrintAndLogEx(WARNING, "smart card response timeout"); if (!silent) PrintAndLogEx(WARNING, "smart card response timeout");
return -1; return -1;
} }
uint32_t len = resp.arg[0]; uint32_t len = resp.arg[0];
if ( !len ) { if ( !len ) {
PrintAndLogEx(WARNING, "smart card response failed"); if (!silent) PrintAndLogEx(WARNING, "smart card response failed");
return -2; return -2;
} }
memcpy(data, resp.d.asBytes, len); memcpy(data, resp.d.asBytes, len);
if (len >= 2) { if (len >= 2) {
PrintAndLogEx(SUCCESS, "%02X%02X | %s", data[len - 2], data[len - 1], GetAPDUCodeDescription(data[len - 2], data[len - 1])); if (!silent) PrintAndLogEx(SUCCESS, "%02X%02X | %s", data[len - 2], data[len - 1], GetAPDUCodeDescription(data[len - 2], data[len - 1]));
} else { } else {
PrintAndLogEx(SUCCESS, " %d | %s", len, sprint_hex_inrow_ex(data, len, 8)); if (!silent) PrintAndLogEx(SUCCESS, " %d | %s", len, sprint_hex_inrow_ex(data, len, 8));
} }
return len; return len;
} }
static int smart_response(uint8_t *data) { static int smart_responseEx(uint8_t *data, bool silent) {
int datalen = smart_wait(data); int datalen = smart_wait(data, silent);
bool needGetData = false; bool needGetData = false;
if (datalen < 2 ) { if (datalen < 2 ) {
@ -321,14 +355,14 @@ static int smart_response(uint8_t *data) {
if (needGetData) { if (needGetData) {
int len = data[datalen - 1]; int len = data[datalen - 1];
PrintAndLogEx(INFO, "Requesting 0x%02X bytes response", len); if (!silent) PrintAndLogEx(INFO, "Requesting 0x%02X bytes response", len);
uint8_t getstatus[] = {0x00, ISO7816_GETSTATUS, 0x00, 0x00, len}; uint8_t getstatus[] = {0x00, ISO7816_GETSTATUS, 0x00, 0x00, len};
UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}}; UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}};
memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) ); memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) );
clearCommandBuffer(); clearCommandBuffer();
SendCommand(&cStatus); SendCommand(&cStatus);
datalen = smart_wait(data); datalen = smart_wait(data, silent);
if (datalen < 2 ) { if (datalen < 2 ) {
goto out; goto out;
@ -339,7 +373,9 @@ static int smart_response(uint8_t *data) {
// data with ACK // data with ACK
if (datalen == len + 2 + 1) { // 2 - response, 1 - ACK if (datalen == len + 2 + 1) { // 2 - response, 1 - ACK
if (data[0] != ISO7816_GETSTATUS) { if (data[0] != ISO7816_GETSTATUS) {
PrintAndLogEx(ERR, "GetResponse ACK error. len 0x%x | data[0] %02X", len, data[0]); if (!silent) {
PrintAndLogEx(ERR, "GetResponse ACK error. len 0x%x | data[0] %02X", len, data[0]);
}
datalen = 0; datalen = 0;
goto out; goto out;
} }
@ -348,7 +384,9 @@ static int smart_response(uint8_t *data) {
memmove(data, &data[1], datalen); memmove(data, &data[1], datalen);
} else { } else {
// wrong length // wrong length
PrintAndLogEx(WARNING, "GetResponse wrong length. Must be 0x%02X got 0x%02X", len, datalen - 3); if (!silent) {
PrintAndLogEx(WARNING, "GetResponse wrong length. Must be 0x%02X got 0x%02X", len, datalen - 3);
}
} }
} }
} }
@ -357,6 +395,10 @@ out:
return datalen; return datalen;
} }
static int smart_response(uint8_t *data) {
return smart_responseEx(data, false);
}
int CmdSmartRaw(const char *Cmd) { int CmdSmartRaw(const char *Cmd) {
int hexlen = 0; int hexlen = 0;
@ -488,7 +530,7 @@ int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leave
clearCommandBuffer(); clearCommandBuffer();
SendCommand(&c); SendCommand(&c);
int len = smart_response(dataout); int len = smart_responseEx(dataout, true);
if ( len < 0 ) { if ( len < 0 ) {
return 2; return 2;
@ -505,11 +547,10 @@ int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leave
clearCommandBuffer(); clearCommandBuffer();
SendCommand(&c2); SendCommand(&c2);
len = smart_response(dataout); len = smart_responseEx(dataout, true);
} }
*dataoutlen = len; *dataoutlen = len;
return 0; return 0;
} }
@ -679,14 +720,14 @@ int CmdSmartInfo(const char *Cmd){
if (GetATRTA1(card.atr, card.atr_len) == 0x11) if (GetATRTA1(card.atr, card.atr_len) == 0x11)
PrintAndLogEx(INFO, "Using default values..."); PrintAndLogEx(INFO, "Using default values...");
PrintAndLogEx(NORMAL, "\t- Di=%d", Di); PrintAndLogEx(NORMAL, "\t- Di %d", Di);
PrintAndLogEx(NORMAL, "\t- Fi=%d", Fi); PrintAndLogEx(NORMAL, "\t- Fi %d", Fi);
PrintAndLogEx(NORMAL, "\t- F=%.1f MHz", F); PrintAndLogEx(NORMAL, "\t- F %.1f MHz", F);
if (Di && Fi) { if (Di && Fi) {
PrintAndLogEx(NORMAL, "\t- Cycles/ETU=%d", Fi/Di); PrintAndLogEx(NORMAL, "\t- Cycles/ETU %d", Fi/Di);
PrintAndLogEx(NORMAL, "\t- %.1f bits/sec at 4MHz", (float)4000000 / (Fi/Di)); PrintAndLogEx(NORMAL, "\t- %.1f bits/sec at 4MHz", (float)4000000 / (Fi/Di));
PrintAndLogEx(NORMAL, "\t- %.1f bits/sec at Fmax=%.1fMHz", (F * 1000000) / (Fi/Di), F); PrintAndLogEx(NORMAL, "\t- %.1f bits/sec at Fmax (%.1fMHz)", (F * 1000000) / (Fi/Di), F);
} else { } else {
PrintAndLogEx(WARNING, "\t- Di or Fi is RFU."); PrintAndLogEx(WARNING, "\t- Di or Fi is RFU.");
}; };
@ -798,58 +839,210 @@ int CmdSmartList(const char *Cmd) {
int CmdSmartBruteforceSFI(const char *Cmd) { int CmdSmartBruteforceSFI(const char *Cmd) {
char ctmp = tolower(param_getchar(Cmd, 0)); uint8_t cmdp = 0;
if (ctmp == 'h') return usage_sm_brute(); bool errors = false, decodeTLV = false; //, useT0 = false;
uint8_t data[5] = {0x00, 0xB2, 0x00, 0x00, 0x00}; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
PrintAndLogEx(INFO, "Selecting card"); case 'h': return usage_sm_brute();
if ( !smart_select(false) ) { case 't':
return 1; decodeTLV = true;
cmdp++;
break;
/*
case '0':
useT0 = true;
cmdp++;
break;
*/
default:
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true;
break;
}
} }
PrintAndLogEx(INFO, "Selecting PPSE aid"); //Validations
CmdSmartRaw("s 0 t d 00a404000e325041592e5359532e4444463031"); if (errors) return usage_sm_brute();
CmdSmartRaw("0 t d 00a4040007a000000004101000"); // mastercard
// CmdSmartRaw("0 t d 00a4040007a0000000031010"); // visa const char *SELECT = "00a40400%02x%s";
PrintAndLogEx(INFO, "starting"); uint8_t READ_RECORD[] = {0x00, 0xB2, 0x00, 0x00, 0x00};
uint8_t GET_PROCESSING_OPTIONS[] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00};
// uint8_t GENERATE_AC[] = {0x80, 0xAE};
// uint8_t GET_CHALLENGE[] = {0x00, 0x84, 0x00};
// uint8_t GET_DATA[] = {0x80, 0xCA, 0x00, 0x00, 0x00};
// uint8_t SELECT[] = {0x00, 0xA4, 0x04, 0x00};
// uint8_t UNBLOCK_PIN[] = {0x84, 0x24, 0x00, 0x00, 0x00};
// uint8_t VERIFY[] = {0x00, 0x20, 0x00, 0x80};
UsbCommand c = {CMD_SMART_RAW, {SC_RAW, sizeof(data), 0}};
// Select AID command
UsbCommand cAid = {CMD_SMART_RAW, {SC_RAW_T0, 0, 0}};
// Get processing options command
UsbCommand cOpt = {CMD_SMART_RAW, {SC_RAW_T0, sizeof(GET_PROCESSING_OPTIONS), 0}};
memcpy(cOpt.d.asBytes, GET_PROCESSING_OPTIONS, sizeof(GET_PROCESSING_OPTIONS) );
// READ RECORD
UsbCommand cSFI = {CMD_SMART_RAW, {SC_RAW_T0, sizeof(READ_RECORD), 0}};
PrintAndLogEx(INFO, "Importing AID list");
json_t *root = NULL;
smart_loadjson("aidlist", "json", &root);
uint8_t* buf = malloc(USB_CMD_DATA_SIZE); uint8_t* buf = malloc(USB_CMD_DATA_SIZE);
if ( !buf ) if ( !buf )
return 1; 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) ); uint8_t* sfibuf = malloc(USB_CMD_DATA_SIZE);
clearCommandBuffer(); if ( !sfibuf )
SendCommand(&c); return 1;
smart_response(buf); PrintAndLogEx(INFO, "Selecting card");
if ( !smart_select(false) )
if ( buf[0] == 0x6C ) { return 1;
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; for (int i = 0; i < json_array_size(root); i++) {
} json_t *data, *jaid;
memset(buf, 0x00, USB_CMD_DATA_SIZE);
data = json_array_get(root, i);
if (!json_is_object(data)) {
PrintAndLogEx(ERR, "data %d is not an object\n", i + 1);
json_decref(root);
return 1;
} }
}
jaid = json_object_get(data, "AID");
if (!json_is_string(jaid)) {
PrintAndLogEx(ERR, "AID data [%d] is not a string", i + 1);
json_decref(root);
return 1;
}
const char* aid = json_string_value(jaid);
if ( !aid ) continue;
size_t aidlen = strlen(aid);
char* caid = calloc( 8+2+aidlen+1, sizeof(uint8_t));
snprintf(caid, 8+2+aidlen+1, SELECT, aidlen >> 1, aid);
int hexlen = 0;
int res = param_gethex_to_eol(caid, 0, cAid.d.asBytes, sizeof(cAid.d.asBytes), &hexlen);
if ( res ) continue;
cAid.arg[1] = hexlen;
clearCommandBuffer();
SendCommand(&cAid);
int len = smart_responseEx(buf, true);
if ( len < 3 ) {
free(caid);
continue;
}
json_t *jvendor, *jname;
jvendor = json_object_get(data, "Vendor");
if (!json_is_string(jvendor)) {
PrintAndLogEx(ERR, "Vendor data [%d] is not a string", i + 1);
continue;
}
const char* vendor = json_string_value(jvendor);
if ( !vendor ) continue;
jname = json_object_get(data, "Name");
if (!json_is_string(jname)) {
PrintAndLogEx(ERR, "Name data [%d] is not a string", i + 1);
continue;
}
const char* name = json_string_value(jname);
if ( !name ) continue;
PrintAndLogEx(SUCCESS, "AID %s | %s | %s", aid, vendor, name);
// Get processing options
clearCommandBuffer();
SendCommand(&cOpt);
uint8_t optionslen = smart_responseEx(buf, true);
if ( optionslen > 4 ) {
PrintAndLogEx(SUCCESS, "Got processing options");
if ( decodeTLV ) {
TLVPrintFromBuffer(buf, optionslen-2);
}
} else {
PrintAndLogEx(FAILED, "Getting processing options failed");
}
PrintAndLogEx(INFO, "Start SFI brute forcing");
for (uint8_t sfi=1; sfi <= 31; sfi++) {
printf("."); fflush(stdout);
for (uint16_t rec=1; rec <= 5; rec++) {
if (ukbhit()) {
int gc = getchar(); (void)gc;
PrintAndLogEx(NORMAL, "\naborted via keyboard!\n");
free(caid);
goto out;
}
READ_RECORD[2] = rec;
READ_RECORD[3] = (sfi << 3) | 4;
memcpy(cSFI.d.asBytes, READ_RECORD, sizeof(READ_RECORD) );
clearCommandBuffer();
SendCommand(&cSFI);
uint8_t sfilen = smart_responseEx(sfibuf, true);
if ( sfibuf[0] == 0x6C ) {
READ_RECORD[4] = sfibuf[1];
memcpy(cSFI.d.asBytes, READ_RECORD, sizeof(READ_RECORD) );
clearCommandBuffer();
SendCommand(&cSFI);
sfilen = smart_responseEx(sfibuf, true);
READ_RECORD[4] = 0;
}
if ( sfilen > 4 ) {
PrintAndLogEx(SUCCESS, "\n\t file %02d, record %02d found", sfi, rec);
uint8_t modifier = (sfibuf[0] == 0xC0) ? 1 : 0;
if ( decodeTLV ) {
if (!TLVPrintFromBuffer(sfibuf + modifier, sfilen-2-modifier)) {
PrintAndLogEx(SUCCESS, "\tHEX: %s", sprint_hex(sfibuf, sfilen));
}
}
}
memset(sfibuf, 0x00, USB_CMD_DATA_SIZE);
}
}
free(sfibuf);
free(caid);
PrintAndLogEx(SUCCESS, "\nSFI brute force done\n");
}
out:
if ( sfibuf )
free(sfibuf);
free(buf); free(buf);
json_decref(root);
PrintAndLogEx(SUCCESS, "Search completed.");
return 0; return 0;
} }

View file

@ -1464,6 +1464,7 @@ int CmdEMVGetrng(const char *Cmd) {
char cmdp = param_getchar(Cmd, 0); char cmdp = param_getchar(Cmd, 0);
if ( cmdp == 'h' || cmdp == 'H') return usage_emv_getrnd(); if ( cmdp == 'h' || cmdp == 'H') return usage_emv_getrnd();
PrintAndLogEx(INFO, "Not implemented yet");
return 0; return 0;
} }

View file

@ -259,6 +259,21 @@ static const struct emv_tag emv_tags[] = {
{ 0x9f4c, "ICC Dynamic Number" }, { 0x9f4c, "ICC Dynamic Number" },
{ 0x9f4d, "Log Entry" }, { 0x9f4d, "Log Entry" },
{ 0x9f4f, "Log Format", EMV_TAG_DOL }, { 0x9f4f, "Log Format", EMV_TAG_DOL },
{ 0x9f50, "Offline Accumulator Balance" },
{ 0x9f51, "Application Currency Code" },
{ 0x9f51, "DRDOL" },
{ 0x9f52, "Application Default Action (ADA)" },
{ 0x9f52, "Terminal Compatibility Indicator" },
{ 0x9f55, "Issuer Authentication Flags" },
{ 0x9f56, "Issuer Authentication Indicator" },
{ 0x9f57, "Issuer Country Code" },
{ 0x9f58, "Consecutive Transaction Counter Limit (CTCL)" },
{ 0x9f59, "Consecutive Transaction Counter Upper Limit (CTCUL)" },
{ 0x9f5A, "Application Program Identifier" },
{ 0x9f5b, "Issuer Script Results" },
{ 0x9f5c, "Cumulative Total Transaction Amount Upper Limit (CTTAUL)" },
{ 0x9f60, "CVC3 (Track1)" }, { 0x9f60, "CVC3 (Track1)" },
{ 0x9f61, "CVC3 (Track2)" }, { 0x9f61, "CVC3 (Track2)" },
{ 0x9f62, "PCVC3(Track1)" }, { 0x9f62, "PCVC3(Track1)" },
@ -278,21 +293,18 @@ static const struct emv_tag emv_tags[] = {
{ 0xdf20, "Issuer Proprietary Bitmap (IPB)" }, { 0xdf20, "Issuer Proprietary Bitmap (IPB)" },
}; };
static int emv_sort_tag(tlv_tag_t tag) static int emv_sort_tag(tlv_tag_t tag) {
{
return (int)(tag >= 0x100 ? tag : tag << 8); return (int)(tag >= 0x100 ? tag : tag << 8);
} }
static int emv_tlv_compare(const void *a, const void *b) static int emv_tlv_compare(const void *a, const void *b) {
{
const struct tlv *tlv = a; const struct tlv *tlv = a;
const struct emv_tag *tag = b; const struct emv_tag *tag = b;
return emv_sort_tag(tlv->tag) - (emv_sort_tag(tag->tag)); return emv_sort_tag(tlv->tag) - (emv_sort_tag(tag->tag));
} }
static const struct emv_tag *emv_get_tag(const struct tlv *tlv) static const struct emv_tag *emv_get_tag(const struct tlv *tlv) {
{
struct emv_tag *tag = bsearch(tlv, emv_tags, sizeof(emv_tags)/sizeof(emv_tags[0]), struct emv_tag *tag = bsearch(tlv, emv_tags, sizeof(emv_tags)/sizeof(emv_tags[0]),
sizeof(emv_tags[0]), emv_tlv_compare); sizeof(emv_tags[0]), emv_tlv_compare);
@ -310,8 +322,7 @@ static const char *bitstrings[] = {
"1.......", "1.......",
}; };
static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
{
const struct emv_tag_bit *bits = tag->data; const struct emv_tag_bit *bits = tag->data;
unsigned bit, byte; unsigned bit, byte;
@ -331,8 +342,7 @@ static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *ta
} }
} }
static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
{
const unsigned char *buf = tlv->value; const unsigned char *buf = tlv->value;
size_t left = tlv->len; size_t left = tlv->len;
@ -353,7 +363,7 @@ 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) {
fprintf(f, "\tString value '"); fprintf(f, "\tString value '");
fwrite(tlv->value, 1, tlv->len, f); fwrite(tlv->value, 1, tlv->len, f);
fprintf(f, "'\n"); fprintf(f, "'\n");
@ -468,8 +478,6 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, F
if (data[0] || data[1] || data[2] || data[3]) if (data[0] || data[1] || data[2] || data[3])
emv_tag_dump_bitmask(&bit_tlv, &bit_tag, f, level); emv_tag_dump_bitmask(&bit_tlv, &bit_tag, f, level);
return;
} }
// EMV Book 3 // EMV Book 3
@ -512,12 +520,9 @@ static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, F
break; break;
} }
} }
return;
} }
static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
{
uint32_t X, Y; uint32_t X, Y;
int i; int i;
@ -615,7 +620,7 @@ 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){ 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) { if (tlv->len < 4 || tlv->len % 4) {
PRINT_INDENT(level); PRINT_INDENT(level);
fprintf(f, "\tINVALID!\n"); fprintf(f, "\tINVALID!\n");
@ -628,8 +633,7 @@ static void emv_tag_dump_afl(const struct tlv *tlv, const struct emv_tag *tag, F
} }
} }
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) {
fprintf(f, "NULL\n"); fprintf(f, "NULL\n");
return false; return false;
@ -682,8 +686,7 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level)
return true; return true;
} }
char *emv_get_tag_name(const struct tlv *tlv) char *emv_get_tag_name(const struct tlv *tlv) {
{
static char *defstr = ""; static char *defstr = "";
if (!tlv) if (!tlv)

View file

@ -120,7 +120,7 @@ static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf)
return true; return true;
} }
void TLVPrintFromBuffer(uint8_t *data, int datalen) { bool 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) {
@ -128,9 +128,11 @@ void TLVPrintFromBuffer(uint8_t *data, int datalen) {
tlvdb_visit(t, print_cb, NULL, 0); tlvdb_visit(t, print_cb, NULL, 0);
tlvdb_free(t); tlvdb_free(t);
return true;
} else { } else {
PrintAndLogEx(WARNING, "TLV ERROR: Can't parse response as TLV tree."); PrintAndLogEx(WARNING, "TLV ERROR: Can't parse response as TLV tree.");
} }
return false;
} }
void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) { void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) {

View file

@ -65,7 +65,7 @@ enum CardPSVendor {
}; };
extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen); extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen);
extern void TLVPrintFromBuffer(uint8_t *data, int datalen); extern bool TLVPrintFromBuffer(uint8_t *data, int datalen);
extern void TLVPrintFromTLV(struct tlvdb *tlv); extern void TLVPrintFromTLV(struct tlvdb *tlv);
extern void TLVPrintFromTLVLev(struct tlvdb *tlv, int level); extern void TLVPrintFromTLVLev(struct tlvdb *tlv, int level);
extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv); extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv);