diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index e543291cc..728aabefe 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -64,16 +64,6 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { furi_string_free(temp_str); } -// static void nfc_scene_emulate_on_enter_emv(NfcApp* instance) { -// const Iso14443_4aData* iso14443_4a_data = -// nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); - -// instance->listener = -// nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); -// nfc_listener_start( -// instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); -// } - const NfcProtocolSupportBase nfc_protocol_support_emv = { .features = NfcProtocolFeatureMoreInfo, diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index a01a0ba68..9e315ff47 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -1,6 +1,7 @@ #include "emv_render.h" #include "../iso14443_4a/iso14443_4a_render.h" +#include #include "nfc/nfc_app_i.h" void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { @@ -29,21 +30,9 @@ 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); + nfc_render_emv_name(data->emv_application.application_name, str); } void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) { @@ -63,11 +52,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; @@ -83,21 +67,23 @@ void nfc_render_emv_country(uint16_t country_code, FuriString* str) { void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { const uint8_t len = apl->aid_len; - if(!len) { - furi_string_cat_printf(str, "No Pay Application found\n"); - return; - } - 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_application_interchange_profile(const EmvApplication* apl, FuriString* str) { + uint16_t data = bit_lib_bytes_to_num_be(apl->application_interchange_profile, 2); + + if(!data) { + furi_string_cat_printf(str, "No Interchange profile found\n"); + return; + } + + furi_string_cat_printf(str, "Interchange profile: "); + for(uint8_t i = 0; i < 2; i++) + furi_string_cat_printf(str, "%02X", apl->application_interchange_profile[i]); + furi_string_cat_printf(str, "\n"); } void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { @@ -126,23 +112,14 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { if(!apl->trans[i].amount) { furi_string_cat_printf(str, "???"); } else { - uint8_t* a = (uint8_t*)&apl->trans[i].amount; - bool top = true; - for(int x = 0; x < 6; x++) { - // cents - if(x == 5) { - furi_string_cat_printf(str, ".%02X", a[x]); - break; - } - if(a[x]) { - if(top) { - furi_string_cat_printf(str, "%X", a[x]); - top = false; - } else { - furi_string_cat_printf(str, "%02X", a[x]); - } - } - } + uint8_t amount_bytes[6]; + bit_lib_num_to_bytes_le(apl->trans[i].amount, 6, amount_bytes); + + bool junk = false; + uint64_t amount = bit_lib_bytes_to_num_bcd(amount_bytes, 6, &junk); + uint8_t amount_cents = amount % 100; + + furi_string_cat_printf(str, "%llu.%02u", amount / 100, amount_cents); } if(apl->trans[i].currency) { @@ -183,8 +160,8 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { void nfc_render_emv_extra(const EmvData* data, FuriString* str) { nfc_render_emv_application(&data->emv_application, str); + nfc_render_emv_application_interchange_profile(&data->emv_application, 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); } diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h index 855acdc4a..4f1e9bb09 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -15,9 +15,9 @@ void nfc_render_emv_name(const char* data, FuriString* str); void nfc_render_emv_application(const EmvApplication* data, FuriString* str); -void nfc_render_emv_extra(const EmvData* data, FuriString* str); +void nfc_render_emv_application_interchange_profile(const EmvApplication* apl, FuriString* str); -void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str); +void nfc_render_emv_extra(const EmvData* data, FuriString* str); void nfc_render_emv_country(uint16_t country_code, FuriString* str); diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index 26bfe265b..663f9a608 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -70,9 +70,11 @@ 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); - else + if(strlen(app.application_label)) { + furi_string_cat_printf(parsed_data, "\e#%s\n", app.application_label); + } else if(strlen(app.application_name)) { + furi_string_cat_printf(parsed_data, "\e#%s\n", app.application_name); + } else furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV"); if(app.pan_len) { @@ -84,25 +86,75 @@ 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); + parsed = true; } - 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.cardholder_name)) { + furi_string_cat_printf(parsed_data, "Cardholder name: %s\n", app.cardholder_name); + parsed = true; + } + + 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); + + parsed = true; + } + + 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); + + parsed = true; + } FuriString* str = furi_string_alloc(); bool storage_readed = emv_get_country_name(app.country_code, str); - if(storage_readed) + if(storage_readed) { furi_string_cat_printf(parsed_data, "Country: %s\n", furi_string_get_cstr(str)); + parsed = true; + } storage_readed = emv_get_currency_name(app.currency_code, str); - if(storage_readed) + if(storage_readed) { furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str)); + parsed = true; + } - if(app.pin_try_counter != 0xFF) + if(app.pin_try_counter != 0xFF) { furi_string_cat_printf(parsed_data, "PIN attempts left: %d\n", app.pin_try_counter); + parsed = true; + } + + if((app.application_interchange_profile[1] >> 6) & 0b1) { + furi_string_cat_printf(parsed_data, "Mobile: yes\n"); + parsed = true; + } + + if(!parsed) furi_string_cat_printf(parsed_data, "No data was parsed\n"); parsed = true; } while(false); diff --git a/applications/main/nfc/scenes/nfc_scene_emv_more_info.c b/applications/main/nfc/scenes/nfc_scene_emv_more_info.c index 0cddce20a..08f373496 100644 --- a/applications/main/nfc/scenes/nfc_scene_emv_more_info.c +++ b/applications/main/nfc/scenes/nfc_scene_emv_more_info.c @@ -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); diff --git a/lib/nfc/protocols/emv/emv.c b/lib/nfc/protocols/emv/emv.c index 4cdacaefe..af208ea10 100644 --- a/lib/nfc/protocols/emv/emv.c +++ b/lib/nfc/protocols/emv/emv.c @@ -76,10 +76,14 @@ 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; + flipper_format_read_string(ff, "Cardholder name", temp_str); + strcpy(app->cardholder_name, furi_string_get_cstr(temp_str)); + + flipper_format_read_string(ff, "Application name", temp_str); + strcpy(app->application_name, furi_string_get_cstr(temp_str)); + + flipper_format_read_string(ff, "Application label", temp_str); + strcpy(app->application_label, furi_string_get_cstr(temp_str)); uint32_t pan_len; if(!flipper_format_read_uint32(ff, "PAN length", &pan_len, 1)) break; @@ -93,15 +97,24 @@ bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) { if(!flipper_format_read_hex(ff, "AID", app->aid, aid_len)) break; + if(!flipper_format_read_hex( + ff, "Application interchange profile", app->application_interchange_profile, 2)) + break; + if(!flipper_format_read_hex(ff, "Country code", (uint8_t*)&app->country_code, 2)) break; if(!flipper_format_read_hex(ff, "Currency code", (uint8_t*)&app->currency_code, 2)) break; 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; + if(!flipper_format_read_uint32(ff, "PIN try counter", &pin_try_counter, 1)) break; app->pin_try_counter = pin_try_counter; parsed = true; @@ -124,7 +137,12 @@ bool emv_save(const EmvData* data, FlipperFormat* ff) { if(!flipper_format_write_comment_cstr(ff, "EMV specific data:\n")) break; - if(!flipper_format_write_string_cstr(ff, "Name", app.name)) break; + if(!flipper_format_write_string_cstr(ff, "Cardholder name", app.cardholder_name)) break; + + if(!flipper_format_write_string_cstr(ff, "Application name", app.application_name)) break; + + if(!flipper_format_write_string_cstr(ff, "Application label", app.application_label)) + break; uint32_t pan_len = app.pan_len; if(!flipper_format_write_uint32(ff, "PAN length", &pan_len, 1)) break; @@ -136,15 +154,25 @@ bool emv_save(const EmvData* data, FlipperFormat* ff) { if(!flipper_format_write_hex(ff, "AID", app.aid, aid_len)) break; + if(!flipper_format_write_hex( + ff, "Application interchange profile", app.application_interchange_profile, 2)) + break; + if(!flipper_format_write_hex(ff, "Country code", (uint8_t*)&app.country_code, 2)) break; 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_uint32(ff, "PIN counter", (uint32_t*)&app.pin_try_counter, 1)) + 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 try counter", (uint32_t*)&app.pin_try_counter, 1)) break; saved = true; diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index 42aa1a703..e09eacaa4 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -10,12 +10,15 @@ extern "C" { #define EMV_REQ_GET_DATA 0x80CA -#define EMV_TAG_APP_TEMPLATE 0x61 +#define UNKNOWN_TAG 0x0B + #define EMV_TAG_AID 0x4F #define EMV_TAG_PRIORITY 0x87 +#define EMV_TAG_APPL_INTERCHANGE_PROFILE 0x82 #define EMV_TAG_PDOL 0x9F38 -#define EMV_TAG_CARD_NAME 0x50 -#define EMV_TAG_FCI 0xBF0C +#define EMV_TAG_APPL_LABEL 0x50 +#define EMV_TAG_APPL_NAME 0x9F12 +#define EMV_TAG_APPL_EFFECTIVE 0x5F25 #define EMV_TAG_PIN_TRY_COUNTER 0x9F17 #define EMV_TAG_LOG_ENTRY 0x9F4D #define EMV_TAG_LOG_FMT 0x9F4F @@ -36,12 +39,19 @@ extern "C" { #define EMV_TAG_COUNTRY_CODE 0x5F28 #define EMV_TAG_CURRENCY_CODE 0x9F42 #define EMV_TAG_CARDHOLDER_NAME 0x5F20 +#define EMV_TAG_CARDHOLDER_NAME_EXTENDED 0x9F0B #define EMV_TAG_TRACK_2_DATA 0x9F6B #define EMV_TAG_GPO_FMT1 0x80 #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[]; @@ -72,12 +82,18 @@ typedef struct { uint8_t priority; uint8_t aid[16]; uint8_t aid_len; - char name[32]; - bool name_found; + uint8_t application_interchange_profile[2]; + char application_name[16 + 1]; + char application_label[16 + 1]; + char cardholder_name[24 + 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; diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 6ca21df1c..9acb854d6 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -186,7 +186,7 @@ static bool emv_poller_detect(NfcGenericEvent event, void* context) { if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { const EmvError error = emv_poller_select_ppse(instance); - protocol_detected = (error == EmvErrorNone); + protocol_detected = (error == EmvErrorNone) && (instance->data->emv_application.aid_len); } return protocol_detected; diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index c237125b2..e72432287 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -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 @@ -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; @@ -147,12 +115,35 @@ static bool 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); - app->name[tlen] = '\0'; - app->name_found = true; + case EMV_TAG_APPL_INTERCHANGE_PROFILE: + furi_check(tlen == 2); + memcpy(app->application_interchange_profile, &buff[i], tlen); success = true; - FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name); + FURI_LOG_T(TAG, "found EMV_TAG_APPL_INTERCHANGE_PROFILE %x: ", tag); + for(size_t x = 0; x < tlen; x++) { + FURI_LOG_RAW_T("%02X ", app->application_interchange_profile[x]); + } + FURI_LOG_RAW_T("\r\n"); + break; + case EMV_TAG_APPL_LABEL: + memcpy(app->application_label, &buff[i], tlen); + app->application_label[tlen] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_APPL_LABEL %x: %s", tag, app->application_label); + break; + case EMV_TAG_APPL_NAME: + furi_check(tlen < sizeof(app->application_name)); + memcpy(app->application_name, &buff[i], tlen); + app->application_name[tlen] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_APPL_NAME %x: %s", tag, app->application_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); @@ -209,11 +200,19 @@ static bool break; } case EMV_TAG_CARDHOLDER_NAME: { - char name[27]; - memcpy(name, &buff[i], tlen); - name[tlen] = '\0'; + if(strlen(app->cardholder_name) > tlen) break; + memcpy(app->cardholder_name, &buff[i], tlen); + app->cardholder_name[tlen] = '\0'; + + // use space char as terminator + for(size_t i = 0; i < tlen; i++) + if(app->cardholder_name[i] == 0x20) { + app->cardholder_name[i] = '\0'; + break; + } + success = true; - FURI_LOG_T(TAG, "found EMV_TAG_CARDHOLDER_NAME %x: %s", tag, name); + FURI_LOG_T(TAG, "found EMV_TAG_CARDHOLDER_NAME %x: %s", tag, app->cardholder_name); break; } case EMV_TAG_PAN: @@ -225,6 +224,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; @@ -406,6 +406,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; @@ -602,6 +632,11 @@ EmvError emv_poller_read_afl(EmvPoller* instance) { FURI_LOG_D(TAG, "Search PAN in SFI"); + uint8_t sfi_2_mask = 0; + uint8_t sfi_3_mask = 0; + + bool pan_fetched = (instance->data->emv_application.pan_len); + // Iterate through all files for(size_t i = 0; i < instance->data->emv_application.afl.size; i += 4) { uint8_t sfi = afl->data[i] >> 3; @@ -609,6 +644,9 @@ EmvError emv_poller_read_afl(EmvPoller* instance) { uint8_t record_end = afl->data[i + 2]; // Iterate through all records in file for(uint8_t record = record_start; record <= record_end; ++record) { + if((sfi == 2) && (record < 8)) FURI_BIT_SET(sfi_2_mask, record); + if((sfi == 3) && (record < 8)) FURI_BIT_SET(sfi_3_mask, record); + error = emv_poller_read_sfi_record(instance, sfi, record); if(error != EmvErrorNone) break; @@ -620,15 +658,41 @@ EmvError emv_poller_read_afl(EmvPoller* instance) { FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record); } - // Some READ RECORD returns 1 byte response 0x12/0x13 (IDK WTF), - // then poller return Timeout to all subsequent requests. - // TODO: remove below lines when it was fixed - if(instance->data->emv_application.pan_len != 0) - return EmvErrorNone; // Card number fetched + if(instance->data->emv_application.pan_len) pan_fetched = true; // Card number fetched } } + bool cardholder_name_fetched = strlen(instance->data->emv_application.cardholder_name); + // Bruteforse files 2-3 + FURI_LOG_T(TAG, "Bruteforce files 2-3"); + for(size_t sfi = 2; sfi <= 3; sfi++) { + // Iterate through records 1-5 in file + for(size_t record = 1; record <= 5; record++) { + // Skip previously readed sfi + if(sfi == 2) { + if((sfi_2_mask >> record) & (0b1)) continue; + } + if(sfi == 3) { + if((sfi_3_mask >> record) & (0b1)) continue; + } - return error; + if(strlen(instance->data->emv_application.cardholder_name)) + cardholder_name_fetched = true; + error = emv_poller_read_sfi_record(instance, sfi, record); + if(error != EmvErrorNone) break; + + if(!emv_decode_response_tlv( + bit_buffer_get_data(instance->rx_buffer), + bit_buffer_get_size_bytes(instance->rx_buffer), + &instance->data->emv_application)) { + error = EmvErrorProtocol; + FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record); + } + } + } + if(pan_fetched || cardholder_name_fetched) + return EmvErrorNone; + else + return error; } static EmvError emv_poller_req_get_data(EmvPoller* instance, uint16_t tag) { diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c index 2065b81a2..45f427dc6 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -7,6 +7,7 @@ #define TAG "Iso14443_4aPoller" #define ISO14443_4A_FSDI_256 (0x8U) +#define ISO14443_4A_SEND_BLOCK_MAX_ATTEMPTS (20) Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) { furi_assert(instance); @@ -88,7 +89,7 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( BitBuffer* rx_buffer) { furi_assert(instance); - uint8_t retry = 5; + uint8_t attempts_left = ISO14443_4A_SEND_BLOCK_MAX_ATTEMPTS; bit_buffer_reset(instance->tx_buffer); iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); @@ -103,6 +104,8 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( iso14443_4a_get_fwt_fc_max(instance->data)); if(iso14443_3a_error != Iso14443_3aErrorNone) { + FURI_LOG_T( + TAG, "Attempt: %u", ISO14443_4A_SEND_BLOCK_MAX_ATTEMPTS + 1 - attempts_left); FURI_LOG_RAW_T("RAW RX(%d):", bit_buffer_get_size_bytes(instance->rx_buffer)); for(size_t x = 0; x < bit_buffer_get_size_bytes(instance->rx_buffer); x++) { FURI_LOG_RAW_T("%02X ", bit_buffer_get_byte(instance->rx_buffer, x)); @@ -116,7 +119,7 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( error = iso14443_4_layer_decode_block_pwt_ext( instance->iso14443_4_layer, rx_buffer, instance->rx_buffer); if(error == Iso14443_4aErrorSendExtra) { - if(--retry == 0) break; + if(--attempts_left == 0) break; // Send response for Control message if(bit_buffer_get_size_bytes(rx_buffer)) bit_buffer_copy(instance->tx_buffer, rx_buffer);