diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv.c b/applications/main/nfc/helpers/protocol_support/emv/emv.c index 035f8d220..0b60bea6e 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv.c @@ -14,8 +14,8 @@ static void nfc_scene_info_on_enter_emv(NfcApp* instance) { const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); FuriString* temp_str = furi_string_alloc(); - furi_string_cat_printf( - temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + // furi_string_cat_printf( + // temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_emv_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -54,8 +54,8 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) { const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv); FuriString* temp_str = furi_string_alloc(); - furi_string_cat_printf( - temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + // furi_string_cat_printf( + // temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_emv_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( 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 46cdc974f..ead426a15 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -3,15 +3,11 @@ #include "../iso14443_4a/iso14443_4a_render.h" void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) { - nfc_render_iso14443_4a_brief(emv_get_base_data(data), 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_pan(data->emv_application.pan, data->emv_application.pan_len, str); + nfc_render_emv_expired(&data->emv_application, str); - if(format_type != NfcProtocolFormatTypeFull) return; - - furi_string_cat(str, "\n\e#ISO14443-4 data"); - nfc_render_iso14443_4a_extra(emv_get_base_data(data), str); + if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str); } void nfc_render_emv_data(const EmvData* data, FuriString* str) { @@ -20,16 +16,49 @@ void nfc_render_emv_data(const EmvData* data, FuriString* str) { } void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) { - for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%u", data[i]); + if(len == 0) return; + for(uint8_t i = 0; i < len; i += 2) { + furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]); + } 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(const EmvApplication* apl, FuriString* str) { + UNUSED(apl); + UNUSED(str); + // nfc/assets/currency_code.nfc +} + +void nfc_render_emv_country(const EmvApplication* apl, FuriString* str) { + UNUSED(apl); + UNUSED(str); + // nfc/assets/country_code.nfc +} + void nfc_render_emv_name(const char* data, FuriString* str) { - UNUSED(data); + if(strlen(data) == 0) return; + furi_string_cat_printf(str, "\e#"); + furi_string_cat(str, data); furi_string_cat_printf(str, "\n"); } -void nfc_render_emv_application(const EmvApplication* data, FuriString* str) { - UNUSED(data); +void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) { + const uint8_t len = apl->aid_len; + if(len) { + furi_string_cat_printf(str, "AID: "); + for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]); + // nfc/assets/aid.nfc + } else { + furi_string_cat_printf(str, "No Pay Application found"); + } furi_string_cat_printf(str, "\n"); -} \ No newline at end of file +} + +void nfc_render_emv_extra(const EmvData* data, FuriString* str) { + nfc_render_emv_application(&data->emv_application, 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 16fc2e172..8fb31eaea 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.h +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.h @@ -13,4 +13,12 @@ void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) void nfc_render_emv_name(const char* data, FuriString* str); -void nfc_render_emv_application(const EmvApplication* data, FuriString* str); \ No newline at end of file +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(const EmvApplication* apl, FuriString* str); + +void nfc_render_emv_currency(const EmvApplication* apl, FuriString* str); diff --git a/lib/nfc/protocols/emv/emv.h b/lib/nfc/protocols/emv/emv.h index 45318292b..913bdb0cb 100644 --- a/lib/nfc/protocols/emv/emv.h +++ b/lib/nfc/protocols/emv/emv.h @@ -41,7 +41,7 @@ typedef struct { bool app_started; char name[32]; bool name_found; - uint8_t pan[10]; + uint8_t pan[10]; // card_number uint8_t pan_len; uint8_t exp_month; uint8_t exp_year; diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 61ef1c30e..41ae8afba 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -71,7 +71,6 @@ static NfcCommand emv_poller_handler_select_ppse(EmvPoller* instance) { instance->state = EmvPollerStateSelectApplication; } else { FURI_LOG_E(TAG, "Failed to select PPSE"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFailed; } @@ -86,7 +85,6 @@ static NfcCommand emv_poller_handler_select_application(EmvPoller* instance) { instance->state = EmvPollerStateGetProcessingOptions; } else { FURI_LOG_E(TAG, "Failed to select application"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFailed; } @@ -98,10 +96,14 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) if(instance->error == EmvErrorNone) { FURI_LOG_D(TAG, "Get processing options success"); - instance->state = EmvPollerStateReadSuccess; + if(instance->data->emv_application.pan_len > 0) { + instance->state = EmvPollerStateReadSuccess; + } else { + FURI_LOG_D(TAG, "No AFL still. Fallback to bruteforce files"); + instance->state = EmvPollerStateReadFiles; + } } else { FURI_LOG_E(TAG, "Failed to get processing options"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFiles; } @@ -116,7 +118,6 @@ static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { instance->state = EmvPollerStateReadSuccess; } else { FURI_LOG_E(TAG, "Failed to read files"); - iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->state = EmvPollerStateReadFailed; } @@ -133,7 +134,7 @@ static NfcCommand emv_poller_handler_read_fail(EmvPoller* instance) { } static NfcCommand emv_poller_handler_read_success(EmvPoller* instance) { - FURI_LOG_D(TAG, "Read success."); + FURI_LOG_D(TAG, "Read success"); iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->emv_event.type = EmvPollerEventTypeReadSuccess; NfcCommand command = instance->callback(instance->general_event, instance->context); diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index da8503744..739747296 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -183,6 +183,7 @@ static bool emv_decode_response(const uint8_t* buff, uint16_t len, EmvApplicatio break; } case EMV_TAG_TRACK_2_EQUIV: { + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x", tag); // 0xD0 delimits PAN from expiry (YYMM) for(int x = 1; x < tlen; x++) { if(buff[i + x + 1] > 0xD0) { @@ -194,41 +195,45 @@ 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'; + // // 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); success = true; - FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv); break; } case EMV_TAG_PAN: memcpy(app->pan, &buff[i], tlen); app->pan_len = tlen; success = true; + FURI_LOG_T(TAG, "found EMV_TAG_PAN %x", tag); break; case EMV_TAG_EXP_DATE: app->exp_year = buff[i]; app->exp_month = buff[i + 1]; success = true; + FURI_LOG_T(TAG, "found EMV_TAG_EXP_DATE %x", tag); break; case EMV_TAG_CURRENCY_CODE: app->currency_code = (buff[i] << 8 | buff[i + 1]); success = true; + FURI_LOG_T(TAG, "found EMV_TAG_CURRENCY_CODE %x", tag); break; case EMV_TAG_COUNTRY_CODE: app->country_code = (buff[i] << 8 | buff[i + 1]); success = true; + FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag); break; } } @@ -413,6 +418,7 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re emv_trace(instance, "SFI record:"); if(iso14443_4a_error != Iso14443_4aErrorNone) { + FURI_LOG_E(TAG, "Failed to read SFI %d record %d", sfi, record_num); error = emv_process_error(iso14443_4a_error); break; } @@ -423,8 +429,9 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re buff, bit_buffer_get_size_bytes(instance->rx_buffer), &instance->data->emv_application)) { - error = EmvErrorProtocol; - FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num); + // It's ok while bruteforcing + //error = EmvErrorProtocol; + FURI_LOG_T(TAG, "Failed to parse SFI %d record %d", sfi, record_num); } } while(false); @@ -449,8 +456,12 @@ EmvError emv_poller_read_files(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) { - error |= emv_poller_read_sfi_record(instance, sfi, record); + error = emv_poller_read_sfi_record(instance, sfi, record); + if(error != EmvErrorNone) break; + if(instance->data->emv_application.pan_len != 0) + return EmvErrorNone; // Card number fetched } + error = EmvErrorProtocol; } return error; @@ -462,15 +473,19 @@ EmvError emv_poller_read(EmvPoller* instance) { memset(&instance->data->emv_application, 0, sizeof(EmvApplication)); do { - error |= emv_poller_select_ppse(instance); + error = emv_poller_select_ppse(instance); if(error != EmvErrorNone) break; - error |= emv_poller_select_application(instance); + error = emv_poller_select_application(instance); if(error != EmvErrorNone) break; - if(emv_poller_get_processing_options(instance) != EmvErrorNone) + error = emv_poller_get_processing_options(instance); + if(error != EmvErrorNone) break; + + if(instance->data->emv_application.pan_len == 0) { error = emv_poller_read_files(instance); - + if(error != EmvErrorNone) break; + } } while(false); return error;