mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-22 20:43:07 +00:00
Track2 support
Co-authored-by: Nikita Vostokov <1042932+wosk@users.noreply.github.com>
This commit is contained in:
parent
5f041a22e5
commit
84abb53712
3 changed files with 66 additions and 23 deletions
|
@ -890,10 +890,10 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||
else
|
||||
furi_string_cat_printf(parsed_data, "\e#%s", "EMV");
|
||||
|
||||
furi_string_cat_printf(parsed_data, "\nPAN: ");
|
||||
furi_string_cat_printf(parsed_data, "\nPAN:");
|
||||
for(uint8_t i = 0; i < app.pan_len; i++) {
|
||||
if((i % 2 == 0)) furi_string_cat_printf(parsed_data, " ");
|
||||
furi_string_cat_printf(parsed_data, "%02X", app.pan[i]);
|
||||
if((i != 0) && (i % 2 != 0)) furi_string_cat_printf(parsed_data, " ");
|
||||
}
|
||||
|
||||
furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X", app.exp_month, app.exp_year);
|
||||
|
|
|
@ -14,7 +14,7 @@ extern "C" {
|
|||
#define EMV_TAG_PDOL 0x9F38
|
||||
#define EMV_TAG_CARD_NAME 0x50
|
||||
#define EMV_TAG_FCI 0xBF0C
|
||||
#define EMV_TAG_LOG_CTRL 0x9F4D
|
||||
#define EMV_TAG_LOG_ENTRY 0x9F4D
|
||||
#define EMV_TAG_TRACK_1_EQUIV 0x56
|
||||
#define EMV_TAG_TRACK_2_EQUIV 0x57
|
||||
#define EMV_TAG_PAN 0x5A
|
||||
|
@ -23,6 +23,10 @@ extern "C" {
|
|||
#define EMV_TAG_COUNTRY_CODE 0x5F28
|
||||
#define EMV_TAG_CURRENCY_CODE 0x9F42
|
||||
#define EMV_TAG_CARDHOLDER_NAME 0x5F20
|
||||
#define EMV_TAG_TRACK_2_DATA 0x9F6B
|
||||
|
||||
#define EMV_TAG_RESP_BUF_SIZE 0x6C
|
||||
#define EMV_TAG_GPO_FMT1 0x80
|
||||
|
||||
typedef struct {
|
||||
uint16_t tag;
|
||||
|
@ -35,6 +39,8 @@ typedef struct {
|
|||
} APDU;
|
||||
|
||||
typedef struct {
|
||||
uint8_t log_sfi;
|
||||
uint8_t log_records;
|
||||
uint8_t priority;
|
||||
uint8_t aid[16];
|
||||
uint8_t aid_len;
|
||||
|
|
|
@ -145,15 +145,34 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio
|
|||
}
|
||||
} else {
|
||||
switch(tag) {
|
||||
case EMV_TAG_GPO_FMT1:
|
||||
// skip AIP
|
||||
i += 2;
|
||||
tlen -= 2;
|
||||
memcpy(app->afl.data, &buff[i], tlen);
|
||||
app->afl.size = tlen;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_GPO_FMT1 %X: ", tag);
|
||||
break;
|
||||
case EMV_TAG_RESP_BUF_SIZE:
|
||||
//success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_RESP_BUF_SIZE %X: %d", tag, buff[i]);
|
||||
// Need to request SFI again with this length value
|
||||
break;
|
||||
case EMV_TAG_AID:
|
||||
app->aid_len = tlen;
|
||||
memcpy(app->aid, &buff[i], tlen);
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag);
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_AID %X: ", tag);
|
||||
for(size_t x = 0; x < tlen; x++) {
|
||||
FURI_LOG_RAW_T("%02X ", app->aid[x]);
|
||||
}
|
||||
FURI_LOG_RAW_T("\r\n");
|
||||
break;
|
||||
case EMV_TAG_PRIORITY:
|
||||
memcpy(&app->priority, &buff[i], tlen);
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_APP_PRIORITY %X: %d", tag, app->priority);
|
||||
break;
|
||||
case EMV_TAG_CARD_NAME:
|
||||
memcpy(app->name, &buff[i], tlen);
|
||||
|
@ -174,7 +193,9 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio
|
|||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen);
|
||||
break;
|
||||
// Tracks data https://murdoch.is/papers/defcon20emvdecode.pdf
|
||||
case EMV_TAG_TRACK_1_EQUIV: {
|
||||
// Contain PAN and expire date
|
||||
char track_1_equiv[80];
|
||||
memcpy(track_1_equiv, &buff[i], tlen);
|
||||
track_1_equiv[tlen] = '\0';
|
||||
|
@ -182,8 +203,9 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio
|
|||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv);
|
||||
break;
|
||||
}
|
||||
case EMV_TAG_TRACK_2_DATA:
|
||||
case EMV_TAG_TRACK_2_EQUIV: {
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x", tag);
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X", tag);
|
||||
// 0xD0 delimits PAN from expiry (YYMM)
|
||||
for(int x = 1; x < tlen; x++) {
|
||||
if(buff[i + x + 1] > 0xD0) {
|
||||
|
@ -195,21 +217,21 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio
|
|||
}
|
||||
}
|
||||
|
||||
// // Convert 4-bit to ASCII representation
|
||||
// char track_2_equiv[41];
|
||||
// uint8_t track_2_equiv_len = 0;
|
||||
// for(int x = 0; x < tlen; x++) {
|
||||
// char top = (buff[i + x] >> 4) + '0';
|
||||
// char bottom = (buff[i + x] & 0x0F) + '0';
|
||||
// track_2_equiv[x * 2] = top;
|
||||
// track_2_equiv_len++;
|
||||
// if(top == '?') break;
|
||||
// track_2_equiv[x * 2 + 1] = bottom;
|
||||
// track_2_equiv_len++;
|
||||
// if(bottom == '?') break;
|
||||
// }
|
||||
// track_2_equiv[track_2_equiv_len] = '\0';
|
||||
// FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv);
|
||||
// Convert 4-bit to ASCII representation
|
||||
char track_2_equiv[41];
|
||||
uint8_t track_2_equiv_len = 0;
|
||||
for(int x = 0; x < tlen; x++) {
|
||||
char top = (buff[i + x] >> 4) + '0';
|
||||
char bottom = (buff[i + x] & 0x0F) + '0';
|
||||
track_2_equiv[x * 2] = top;
|
||||
track_2_equiv_len++;
|
||||
if(top == '?') break;
|
||||
track_2_equiv[x * 2 + 1] = bottom;
|
||||
track_2_equiv_len++;
|
||||
if(bottom == '?') break;
|
||||
}
|
||||
track_2_equiv[track_2_equiv_len] = '\0';
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X : %s", tag, track_2_equiv);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
@ -235,6 +257,17 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio
|
|||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag);
|
||||
break;
|
||||
case EMV_TAG_LOG_ENTRY:
|
||||
app->log_sfi = buff[i];
|
||||
app->log_records = buff[i + 1];
|
||||
success = true;
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"found EMV_TAG_LOG_ENTRY %x: sfi 0x%x, records %d",
|
||||
tag,
|
||||
app->log_sfi,
|
||||
app->log_records);
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += tlen;
|
||||
|
@ -392,6 +425,7 @@ EmvError emv_poller_get_processing_options(EmvPoller* instance) {
|
|||
|
||||
EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num) {
|
||||
EmvError error = EmvErrorNone;
|
||||
FuriString* text = furi_string_alloc();
|
||||
|
||||
uint8_t sfi_param = (sfi << 3) | (1 << 2);
|
||||
uint8_t emv_sfi_header[] = {
|
||||
|
@ -411,10 +445,11 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re
|
|||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
emv_trace(instance, "SFI record:");
|
||||
furi_string_printf(text, "SFI 0x%X record %d:", sfi, record_num);
|
||||
emv_trace(instance, furi_string_get_cstr(text));
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read SFI %d record %d", sfi, record_num);
|
||||
FURI_LOG_E(TAG, "Failed to read SFI 0x%X record %d", sfi, record_num);
|
||||
error = emv_process_error(iso14443_4a_error);
|
||||
break;
|
||||
}
|
||||
|
@ -427,10 +462,12 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re
|
|||
&instance->data->emv_application)) {
|
||||
// It's ok while bruteforcing
|
||||
//error = EmvErrorProtocol;
|
||||
FURI_LOG_T(TAG, "Failed to parse SFI %d record %d", sfi, record_num);
|
||||
FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record_num);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
furi_string_free(text);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue