Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emv fixes #702

Merged
merged 29 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9ceae3a
[EMV] Fix crash while PDOL parsing
wosk Feb 2, 2024
b167471
[EMV] Add Effective date and Preferred Name
wosk Feb 2, 2024
ec35662
code cleanup, gui fixes
Leptopt1los Feb 2, 2024
d195de5
Fix PDOL parsing
wosk Feb 8, 2024
e6935f2
Reset transactions widget before enter again
wosk Feb 10, 2024
ab609bc
WIP: Test for some visa cards
wosk Feb 10, 2024
1e09034
Merge remote-tracking branch 'upstream/dev' into emv-fixes
Leptopt1los Feb 10, 2024
56875ed
Revert "WIP: Test for some visa cards"
Leptopt1los Feb 10, 2024
702e4f3
0x5F25 tag renamed (issue date->effective date)
Leptopt1los Feb 10, 2024
c35f258
parser 0day fix
Leptopt1los Feb 10, 2024
c24625f
14_4a poller: send block max attempts increased
Leptopt1los Feb 10, 2024
6f71f11
Merge remote-tracking branch 'upstream/dev' into emv-fixes
Leptopt1los Feb 10, 2024
4a382bc
typos fixed
Leptopt1los Feb 10, 2024
08f096d
EMV dump save/load fix
Leptopt1los Feb 11, 2024
a9de06d
cardholder name parsing prepared
Leptopt1los Feb 11, 2024
809e1b3
explicit nodata message when no data parsed from card
Leptopt1los Feb 11, 2024
11cfbd1
bruteforce sfi 2-3 records 1-5
Leptopt1los Feb 12, 2024
14d3510
little speed up
Leptopt1los Feb 12, 2024
b904555
application interchange profile parse added
Leptopt1los Feb 12, 2024
5c88e68
Merge remote-tracking branch 'upstream/dev' into emv-fixes
Leptopt1los Feb 13, 2024
3f29295
transactions render fix
Leptopt1los Feb 14, 2024
0397dd0
fix false-positive emv protocol detect
Leptopt1los Feb 14, 2024
8dd59a5
Merge remote-tracking branch 'upstream/dev' into emv-fixes
Leptopt1los Feb 14, 2024
c061fb1
transactions render fix part 2
Leptopt1los Feb 14, 2024
7b01a33
read transactions fail on some cards fixed
Leptopt1los Feb 15, 2024
4d73794
emv parser day ??->dd
Leptopt1los Feb 15, 2024
f7742c6
Merge remote-tracking branch 'upstream/dev' into emv-fixes
Leptopt1los Feb 15, 2024
698d0b4
emv parser day dd->--
Leptopt1los Feb 15, 2024
4adb492
nevermind. target 18
Leptopt1los Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions applications/main/nfc/helpers/protocol_support/emv/emv_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,6 @@ void nfc_render_emv_uid(const uint8_t* uid, const uint8_t uid_len, FuriString* s
furi_string_cat_printf(str, "\n");
}

void nfc_render_emv_aid(const uint8_t* uid, const uint8_t uid_len, FuriString* str) {
if(uid_len == 0) return;

furi_string_cat_printf(str, "UID: ");

for(uint8_t i = 0; i < uid_len; i++) {
furi_string_cat_printf(str, "%02X ", uid[i]);
}

furi_string_cat_printf(str, "\n");
}

void nfc_render_emv_data(const EmvData* data, FuriString* str) {
nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str);
nfc_render_emv_name(data->emv_application.name, str);
Expand All @@ -63,11 +51,6 @@ void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str)
furi_string_cat_printf(str, "\n");
}

void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str) {
if(apl->exp_month == 0) return;
furi_string_cat_printf(str, "Exp: %02X/%02X\n", apl->exp_month, apl->exp_year);
}

void nfc_render_emv_currency(uint16_t cur_code, FuriString* str) {
if(!cur_code) return;

Expand All @@ -89,17 +72,10 @@ void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) {
}

furi_string_cat_printf(str, "AID: ");

for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]);

furi_string_cat_printf(str, "\n");
}

static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) {
if(counter == 0xff) return;
furi_string_cat_printf(str, "PIN attempts left: %d\n", counter);
}

void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) {
if(apl->transaction_counter)
furi_string_cat_printf(str, "Transactions count: %d\n", apl->transaction_counter);
Expand Down Expand Up @@ -186,5 +162,4 @@ void nfc_render_emv_extra(const EmvData* data, FuriString* str) {

nfc_render_emv_currency(data->emv_application.currency_code, str);
nfc_render_emv_country(data->emv_application.country_code, str);
nfc_render_emv_pin_try_counter(data->emv_application.pin_try_counter, str);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ void nfc_render_emv_application(const EmvApplication* data, FuriString* str);

void nfc_render_emv_extra(const EmvData* data, FuriString* str);

void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str);

void nfc_render_emv_country(uint16_t country_code, FuriString* str);

void nfc_render_emv_currency(uint16_t cur_code, FuriString* str);
Expand Down
37 changes: 33 additions & 4 deletions applications/main/nfc/plugins/supported_cards/emv.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) {
const EmvApplication app = data->emv_application;

do {
if(app.name_found)
furi_string_cat_printf(parsed_data, "\e#%s\n", app.name);
if(strlen(app.payment_sys))
furi_string_cat_printf(parsed_data, "\e#%s\n", app.payment_sys);
else
furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV");

Expand All @@ -87,12 +87,41 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) {
// Cut padding 'F' from card number
size_t end = furi_string_search_rchar(pan, 'F');
if(end) furi_string_left(pan, end);
furi_string_cat_printf(pan, "\n");
furi_string_cat(parsed_data, pan);

furi_string_free(pan);
}

if(app.exp_month | app.exp_year)
furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X\n", app.exp_month, app.exp_year);
if(strlen(app.name)) furi_string_cat_printf(parsed_data, "Name: %s\n", app.name);

if(app.effective_month) {
char day[] = "??";
if(app.effective_day) itoa(app.effective_day, day, 16);
if(day[1] == '\0') {
day[1] = day[0];
day[0] = '0';
}

furi_string_cat_printf(
parsed_data,
"Effective: %s.%02X.20%02X\n",
day,
app.effective_month,
app.effective_year);
}

if(app.exp_month) {
char day[] = "??";
if(app.exp_day) itoa(app.exp_day, day, 16);
if(day[1] == '\0') {
day[1] = day[0];
day[0] = '0';
}

furi_string_cat_printf(
parsed_data, "Expires: %s.%02X.20%02X\n", day, app.exp_month, app.exp_year);
}

FuriString* str = furi_string_alloc();
bool storage_readed = emv_get_country_name(app.country_code, str);
Expand Down
2 changes: 2 additions & 0 deletions applications/main/nfc/scenes/nfc_scene_emv_more_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ bool nfc_scene_emv_more_info_on_event(void* context, SceneManagerEvent event) {
const EmvData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolEmv);

if(event.type == SceneManagerEventTypeCustom) {
widget_reset(nfc->widget);

if(event.event == SubmenuIndexTransactions) {
FuriString* temp_str = furi_string_alloc();
nfc_render_emv_transactions(&data->emv_application, temp_str);
Expand Down
21 changes: 18 additions & 3 deletions lib/nfc/protocols/emv/emv.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) {

EmvApplication* app = &data->emv_application;

//Read name
if(!flipper_format_read_string(ff, "Name", temp_str)) break;
strcpy(app->name, furi_string_get_cstr(temp_str));
if(app->name[0] != '\0') app->name_found = true;

//Read label
if(!flipper_format_read_string(ff, "Payment system", temp_str)) break;
strcpy(app->payment_sys, furi_string_get_cstr(temp_str));

uint32_t pan_len;
if(!flipper_format_read_uint32(ff, "PAN length", &pan_len, 1)) break;
Expand All @@ -99,6 +101,11 @@ bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) {

if(!flipper_format_read_hex(ff, "Expiration year", &app->exp_year, 1)) break;
if(!flipper_format_read_hex(ff, "Expiration month", &app->exp_month, 1)) break;
if(!flipper_format_read_hex(ff, "Expiration day", &app->exp_day, 1)) break;

if(!flipper_format_read_hex(ff, "Effective year", &app->effective_year, 1)) break;
if(!flipper_format_read_hex(ff, "Effective month", &app->effective_month, 1)) break;
if(!flipper_format_read_hex(ff, "Effective day", &app->effective_day, 1)) break;

uint32_t pin_try_counter;
if(!flipper_format_read_uint32(ff, "PIN counter", &pin_try_counter, 1)) break;
Expand Down Expand Up @@ -126,6 +133,8 @@ bool emv_save(const EmvData* data, FlipperFormat* ff) {

if(!flipper_format_write_string_cstr(ff, "Name", app.name)) break;

if(!flipper_format_write_string_cstr(ff, "Payment system", app.payment_sys)) break;

uint32_t pan_len = app.pan_len;
if(!flipper_format_write_uint32(ff, "PAN length", &pan_len, 1)) break;

Expand All @@ -141,8 +150,14 @@ bool emv_save(const EmvData* data, FlipperFormat* ff) {
if(!flipper_format_write_hex(ff, "Currency code", (uint8_t*)&app.currency_code, 2)) break;

if(!flipper_format_write_hex(ff, "Expiration year", (uint8_t*)&app.exp_year, 1)) break;

if(!flipper_format_write_hex(ff, "Expiration month", (uint8_t*)&app.exp_month, 1)) break;
if(!flipper_format_write_hex(ff, "Expiration day", (uint8_t*)&app.exp_day, 1)) break;

if(!flipper_format_write_hex(ff, "Effective year", (uint8_t*)&app.effective_year, 1))
break;
if(!flipper_format_write_hex(ff, "Effective month", (uint8_t*)&app.effective_month, 1))
break;
if(!flipper_format_write_hex(ff, "Effective day", (uint8_t*)&app.effective_day, 1)) break;

if(!flipper_format_write_uint32(ff, "PIN counter", (uint32_t*)&app.pin_try_counter, 1))
break;
Expand Down
22 changes: 16 additions & 6 deletions lib/nfc/protocols/emv/emv.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ extern "C" {

#define EMV_REQ_GET_DATA 0x80CA

#define EMV_TAG_APP_TEMPLATE 0x61
#define EMV_TAG_AID 0x4F
#define EMV_TAG_PRIORITY 0x87
#define EMV_TAG_PDOL 0x9F38
#define EMV_TAG_CARD_NAME 0x50
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_PIN_TRY_COUNTER 0x9F17
#define EMV_TAG_APPL_PAYMENT_SYS 0x50
#define EMV_TAG_APPL_NAME 0x9F12
#define EMV_TAG_APPL_EFFECTIVE 0x5F25
#define EMV_TAG_PIN_ATTEMPTS_COUNTER 0x9F17
#define EMV_TAG_LOG_ENTRY 0x9F4D
#define EMV_TAG_LOG_FMT 0x9F4F

Expand All @@ -42,6 +42,12 @@ extern "C" {
#define EMV_TAG_RESP_BUF_SIZE 0x6C
#define EMV_TAG_RESP_BYTES_AVAILABLE 0x61

// Not used tags
#define EMV_TAG_FORM_FACTOR 0x9F6E
#define EMV_TAG_APP_TEMPLATE 0x61
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_DEPOSIT_LOG_ENTRY 0xDF4D

typedef struct {
uint16_t tag;
uint8_t data[];
Expand Down Expand Up @@ -72,12 +78,16 @@ typedef struct {
uint8_t priority;
uint8_t aid[16];
uint8_t aid_len;
char name[32];
bool name_found;
char name[16 + 1];
char payment_sys[16 + 1];
uint8_t pan[10]; // card_number
uint8_t pan_len;
uint8_t exp_day;
uint8_t exp_month;
uint8_t exp_year;
uint8_t effective_day;
uint8_t effective_month;
uint8_t effective_year;
uint16_t country_code;
uint16_t currency_code;
uint8_t pin_try_counter;
Expand Down
2 changes: 1 addition & 1 deletion lib/nfc/protocols/emv/emv_poller.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) {

static NfcCommand emv_poller_handler_read_extra_data(EmvPoller* instance) {
emv_poller_get_last_online_atc(instance);
emv_poller_get_pin_try_counter(instance);
emv_poller_get_pin_attempts_counter(instance);

instance->state = EmvPollerStateReadSuccess;
return NfcCommandContinue;
Expand Down
2 changes: 1 addition & 1 deletion lib/nfc/protocols/emv/emv_poller.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ EmvError emv_poller_read_afl(EmvPoller* instance);

EmvError emv_poller_read_log_entry(EmvPoller* instance);

EmvError emv_poller_get_pin_try_counter(EmvPoller* instance);
EmvError emv_poller_get_pin_attempts_counter(EmvPoller* instance);

EmvError emv_poller_get_last_online_atc(EmvPoller* instance);

Expand Down
92 changes: 52 additions & 40 deletions lib/nfc/protocols/emv/emv_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#define TAG "EMVPoller"

// "Terminal" parameters, which could be requested by card
const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information
const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type
const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicator
Expand Down Expand Up @@ -76,39 +77,6 @@ static void emv_trace(EmvPoller* instance, const char* message) {
}
}

static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
bool tag_found;
for(uint16_t i = 0; i < src->size; i++) {
tag_found = false;
for(uint8_t j = 0; j < sizeof(pdol_values) / sizeof(PDOLValue*); j++) {
if(src->data[i] == pdol_values[j]->tag) {
// Found tag with 1 byte length
uint8_t len = src->data[++i];
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
dest->size += len;
tag_found = true;
break;
} else if(((src->data[i] << 8) | src->data[i + 1]) == pdol_values[j]->tag) {
// Found tag with 2 byte length
i += 2;
uint8_t len = src->data[i];
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
dest->size += len;
tag_found = true;
break;
}
}
if(!tag_found) {
// Unknown tag, fill zeros
i += 2;
uint8_t len = src->data[i];
memset(dest->data + dest->size, 0, len);
dest->size += len;
}
}
return dest->size;
}

static bool
emv_decode_tlv_tag(const uint8_t* buff, uint16_t tag, uint8_t tlen, EmvApplication* app) {
uint8_t i = 0;
Expand Down Expand Up @@ -147,12 +115,25 @@ static bool
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_APP_PRIORITY %X: %d", tag, app->priority);
break;
case EMV_TAG_CARD_NAME:
case EMV_TAG_APPL_PAYMENT_SYS:
memcpy(app->payment_sys, &buff[i], tlen);
app->payment_sys[tlen] = '\0';
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_APPL_PAYMENT_SYS %x: %s", tag, app->payment_sys);
break;
case EMV_TAG_APPL_NAME:
furi_check(tlen < sizeof(app->name));
memcpy(app->name, &buff[i], tlen);
app->name[tlen] = '\0';
app->name_found = true;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name);
FURI_LOG_T(TAG, "found EMV_TAG_APPL_NAME %x: %s", tag, app->name);
break;
case EMV_TAG_APPL_EFFECTIVE:
app->effective_year = buff[i];
app->effective_month = buff[i + 1];
app->effective_day = buff[i + 2];
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_APPL_ISSUE %x:", tag);
break;
case EMV_TAG_PDOL:
memcpy(app->pdol.data, &buff[i], tlen);
Expand Down Expand Up @@ -225,6 +206,7 @@ static bool
case EMV_TAG_EXP_DATE:
app->exp_year = buff[i];
app->exp_month = buff[i + 1];
app->exp_day = buff[i + 2];
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_EXP_DATE %x", tag);
break;
Expand Down Expand Up @@ -280,10 +262,10 @@ static bool
memcpy(&app->trans[app->active_tr].time, &buff[i], tlen);
success = true;
break;
case EMV_TAG_PIN_TRY_COUNTER:
case EMV_TAG_PIN_ATTEMPTS_COUNTER:
app->pin_try_counter = buff[i];
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_PIN_TRY_COUNTER %x: %d", tag, app->pin_try_counter);
FURI_LOG_T(TAG, "found EMV_TAG_PIN_ATTEMPTS_COUNTER %x: %d", tag, app->pin_try_counter);
break;
}
return success;
Expand Down Expand Up @@ -406,6 +388,36 @@ static bool emv_decode_response_tlv(const uint8_t* buff, uint8_t len, EmvApplica
return success;
}

static void emv_prepare_pdol(APDU* dest, APDU* src) {
uint16_t tag = 0;
uint8_t tlen = 0;
uint8_t i = 0;
while(i < src->size) {
bool tag_found = false;
if(!emv_parse_tag(src->data, src->size, &tag, &tlen, &i)) {
FURI_LOG_T(TAG, "Parsing PDOL failed at 0x%x", i);
dest->size = 0;
return;
}

furi_check(dest->size + tlen < sizeof(dest->data));
for(uint8_t j = 0; j < COUNT_OF(pdol_values); j++) {
if(tag == pdol_values[j]->tag) {
memcpy(dest->data + dest->size, pdol_values[j]->data, tlen);
dest->size += tlen;
tag_found = true;
break;
}
}

if(!tag_found) {
// Unknown tag, fill zeros
memset(dest->data + dest->size, 0, tlen);
dest->size += tlen;
}
}
}

EmvError emv_poller_select_ppse(EmvPoller* instance) {
EmvError error = EmvErrorNone;

Expand Down Expand Up @@ -671,8 +683,8 @@ static EmvError emv_poller_req_get_data(EmvPoller* instance, uint16_t tag) {
return error;
}

EmvError emv_poller_get_pin_try_counter(EmvPoller* instance) {
return emv_poller_req_get_data(instance, EMV_TAG_PIN_TRY_COUNTER);
EmvError emv_poller_get_pin_attempts_counter(EmvPoller* instance) {
return emv_poller_req_get_data(instance, EMV_TAG_PIN_ATTEMPTS_COUNTER);
}

EmvError emv_poller_get_last_online_atc(EmvPoller* instance) {
Expand Down
Loading