Track2 support

Co-authored-by: Nikita Vostokov <1042932+wosk@users.noreply.github.com>
This commit is contained in:
Methodius 2024-01-23 19:51:59 +09:00
parent 5f041a22e5
commit 84abb53712
No known key found for this signature in database
GPG key ID: 122FA99A00B41679
3 changed files with 66 additions and 23 deletions

View file

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

View file

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

View file

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