mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 22:40:25 +00:00
[FL-2883] NFC: bank card rework reading (#1858)
* nfc: remove bank card save option * nfc: remove bank card save from nfc device * nfc: remove unused function in emv * nfc: try several times to start emv application * nfc: add AID display fallback for bank cards Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
04f5ad83f8
commit
2552278a3d
6 changed files with 76 additions and 89 deletions
|
@ -1,7 +1,6 @@
|
||||||
#include "../nfc_i.h"
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexSave,
|
|
||||||
SubmenuIndexInfo,
|
SubmenuIndexInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,7 +14,6 @@ void nfc_scene_emv_menu_on_enter(void* context) {
|
||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
Submenu* submenu = nfc->submenu;
|
Submenu* submenu = nfc->submenu;
|
||||||
|
|
||||||
submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
|
|
||||||
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
|
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
|
||||||
submenu_set_selected_item(
|
submenu_set_selected_item(
|
||||||
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
|
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
|
||||||
|
@ -28,13 +26,7 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubmenuIndexSave) {
|
if(event.event == SubmenuIndexInfo) {
|
||||||
nfc->dev->format = NfcDeviceSaveFormatBankCard;
|
|
||||||
// Clear device name
|
|
||||||
nfc_device_set_name(nfc->dev, "");
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexInfo) {
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,33 @@ void nfc_scene_emv_read_success_on_enter(void* context) {
|
||||||
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
|
||||||
|
|
||||||
FuriString* temp_str;
|
FuriString* temp_str;
|
||||||
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
|
if(emv_data->name[0] != '\0') {
|
||||||
for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
|
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
|
||||||
furi_string_cat_printf(
|
} else {
|
||||||
temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
|
temp_str = furi_string_alloc_printf("\e#Unknown Bank Card\n");
|
||||||
|
}
|
||||||
|
if(emv_data->number_len) {
|
||||||
|
for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
|
||||||
|
}
|
||||||
|
furi_string_trim(temp_str);
|
||||||
|
} else if(emv_data->aid_len) {
|
||||||
|
furi_string_cat_printf(temp_str, "Can't parse data from app\n");
|
||||||
|
// Parse AID name
|
||||||
|
FuriString* aid_name;
|
||||||
|
aid_name = furi_string_alloc();
|
||||||
|
if(nfc_emv_parser_get_aid_name(
|
||||||
|
nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid_name)) {
|
||||||
|
furi_string_cat_printf(temp_str, "AID: %s", furi_string_get_cstr(aid_name));
|
||||||
|
} else {
|
||||||
|
furi_string_cat_printf(temp_str, "AID: ");
|
||||||
|
for(uint8_t i = 0; i < emv_data->aid_len; i++) {
|
||||||
|
furi_string_cat_printf(temp_str, "%02X", emv_data->aid[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_string_free(aid_name);
|
||||||
}
|
}
|
||||||
furi_string_trim(temp_str);
|
|
||||||
|
|
||||||
// Add expiration date
|
// Add expiration date
|
||||||
if(emv_data->exp_mon) {
|
if(emv_data->exp_mon) {
|
||||||
|
|
|
@ -636,35 +636,7 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
// Leave for backward compatibility
|
||||||
bool saved = false;
|
|
||||||
EmvData* data = &dev->dev_data.emv_data;
|
|
||||||
uint32_t data_temp = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Write Bank card specific data
|
|
||||||
if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break;
|
|
||||||
if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break;
|
|
||||||
if(!flipper_format_write_string_cstr(file, "Name", data->name)) break;
|
|
||||||
if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break;
|
|
||||||
if(data->exp_mon) {
|
|
||||||
uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
|
|
||||||
if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
|
|
||||||
}
|
|
||||||
if(data->country_code) {
|
|
||||||
data_temp = data->country_code;
|
|
||||||
if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break;
|
|
||||||
}
|
|
||||||
if(data->currency_code) {
|
|
||||||
data_temp = data->currency_code;
|
|
||||||
if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break;
|
|
||||||
}
|
|
||||||
saved = true;
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
return saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
EmvData* data = &dev->dev_data.emv_data;
|
EmvData* data = &dev->dev_data.emv_data;
|
||||||
|
@ -1068,7 +1040,7 @@ static bool nfc_device_save_file(
|
||||||
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
||||||
// Write nfc device type
|
// Write nfc device type
|
||||||
if(!flipper_format_write_comment_cstr(
|
if(!flipper_format_write_comment_cstr(
|
||||||
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card"))
|
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic"))
|
||||||
break;
|
break;
|
||||||
nfc_device_prepare_format_string(dev, temp_str);
|
nfc_device_prepare_format_string(dev, temp_str);
|
||||||
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
|
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
|
||||||
|
@ -1083,8 +1055,6 @@ static bool nfc_device_save_file(
|
||||||
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
|
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
|
||||||
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
||||||
if(!nfc_device_save_mifare_df_data(file, dev)) break;
|
if(!nfc_device_save_mifare_df_data(file, dev)) break;
|
||||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
|
||||||
if(!nfc_device_save_bank_card_data(file, dev)) break;
|
|
||||||
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
|
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||||
// Save data
|
// Save data
|
||||||
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
|
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
|
||||||
|
|
|
@ -233,31 +233,51 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
|
||||||
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
// Bank cards require strong field to start application. If we find AID, try at least several
|
||||||
// Read card
|
// times to start EMV application
|
||||||
|
uint8_t start_application_attempts = 0;
|
||||||
|
while(start_application_attempts < 3) {
|
||||||
|
if(nfc_worker->state != NfcWorkerStateRead) break;
|
||||||
|
start_application_attempts++;
|
||||||
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
|
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
|
||||||
if(!emv_read_bank_card(tx_rx, &emv_app)) break;
|
if(emv_read_bank_card(tx_rx, &emv_app)) {
|
||||||
// Copy data
|
FURI_LOG_D(TAG, "Bank card number read from %d attempt", start_application_attempts);
|
||||||
// TODO Set EmvData to reader or like in mifare ultralight!
|
break;
|
||||||
result->number_len = emv_app.card_number_len;
|
} else if(emv_app.aid_len && !emv_app.app_started) {
|
||||||
memcpy(result->number, emv_app.card_number, result->number_len);
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"AID found but failed to start EMV app from %d attempt",
|
||||||
|
start_application_attempts);
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Failed to find AID");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy data
|
||||||
|
if(emv_app.aid_len) {
|
||||||
result->aid_len = emv_app.aid_len;
|
result->aid_len = emv_app.aid_len;
|
||||||
memcpy(result->aid, emv_app.aid, result->aid_len);
|
memcpy(result->aid, emv_app.aid, result->aid_len);
|
||||||
if(emv_app.name_found) {
|
|
||||||
memcpy(result->name, emv_app.name, sizeof(emv_app.name));
|
|
||||||
}
|
|
||||||
if(emv_app.exp_month) {
|
|
||||||
result->exp_mon = emv_app.exp_month;
|
|
||||||
result->exp_year = emv_app.exp_year;
|
|
||||||
}
|
|
||||||
if(emv_app.country_code) {
|
|
||||||
result->country_code = emv_app.country_code;
|
|
||||||
}
|
|
||||||
if(emv_app.currency_code) {
|
|
||||||
result->currency_code = emv_app.currency_code;
|
|
||||||
}
|
|
||||||
read_success = true;
|
read_success = true;
|
||||||
} while(false);
|
}
|
||||||
|
if(emv_app.card_number_len) {
|
||||||
|
result->number_len = emv_app.card_number_len;
|
||||||
|
memcpy(result->number, emv_app.card_number, result->number_len);
|
||||||
|
}
|
||||||
|
if(emv_app.name_found) {
|
||||||
|
memcpy(result->name, emv_app.name, sizeof(emv_app.name));
|
||||||
|
}
|
||||||
|
if(emv_app.exp_month) {
|
||||||
|
result->exp_mon = emv_app.exp_month;
|
||||||
|
result->exp_year = emv_app.exp_year;
|
||||||
|
}
|
||||||
|
if(emv_app.country_code) {
|
||||||
|
result->country_code = emv_app.country_code;
|
||||||
|
}
|
||||||
|
if(emv_app.currency_code) {
|
||||||
|
result->currency_code = emv_app.currency_code;
|
||||||
|
}
|
||||||
|
|
||||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||||
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
||||||
|
|
|
@ -182,7 +182,7 @@ static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
static bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
bool app_aid_found = false;
|
bool app_aid_found = false;
|
||||||
const uint8_t emv_select_ppse_cmd[] = {
|
const uint8_t emv_select_ppse_cmd[] = {
|
||||||
0x00, 0xA4, // SELECT ppse
|
0x00, 0xA4, // SELECT ppse
|
||||||
|
@ -212,8 +212,8 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
return app_aid_found;
|
return app_aid_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
static bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
bool select_app_success = false;
|
app->app_started = false;
|
||||||
const uint8_t emv_select_header[] = {
|
const uint8_t emv_select_header[] = {
|
||||||
0x00,
|
0x00,
|
||||||
0xA4, // SELECT application
|
0xA4, // SELECT application
|
||||||
|
@ -236,7 +236,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||||
emv_trace(tx_rx, "Start application answer:");
|
emv_trace(tx_rx, "Start application answer:");
|
||||||
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
|
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
|
||||||
select_app_success = true;
|
app->app_started = true;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
|
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
FURI_LOG_E(TAG, "Failed to start application");
|
FURI_LOG_E(TAG, "Failed to start application");
|
||||||
}
|
}
|
||||||
|
|
||||||
return select_app_success;
|
return app->app_started;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
|
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
|
||||||
|
@ -367,14 +367,6 @@ static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
return card_num_read;
|
return card_num_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
|
|
||||||
furi_assert(tx_rx);
|
|
||||||
furi_assert(emv_app);
|
|
||||||
memset(emv_app, 0, sizeof(EmvApplication));
|
|
||||||
|
|
||||||
return emv_select_ppse(tx_rx, emv_app);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
|
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
|
||||||
furi_assert(tx_rx);
|
furi_assert(tx_rx);
|
||||||
furi_assert(emv_app);
|
furi_assert(emv_app);
|
||||||
|
|
|
@ -45,6 +45,7 @@ typedef struct {
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
uint8_t aid[16];
|
uint8_t aid[16];
|
||||||
uint8_t aid_len;
|
uint8_t aid_len;
|
||||||
|
bool app_started;
|
||||||
char name[32];
|
char name[32];
|
||||||
bool name_found;
|
bool name_found;
|
||||||
uint8_t card_number[10];
|
uint8_t card_number[10];
|
||||||
|
@ -68,15 +69,6 @@ typedef struct {
|
||||||
*/
|
*/
|
||||||
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
|
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
|
||||||
|
|
||||||
/** Search for EMV Application
|
|
||||||
*
|
|
||||||
* @param tx_rx FuriHalNfcTxRxContext instance
|
|
||||||
* @param emv_app EmvApplication instance
|
|
||||||
*
|
|
||||||
* @return true on success
|
|
||||||
*/
|
|
||||||
bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
|
|
||||||
|
|
||||||
/** Emulate bank card
|
/** Emulate bank card
|
||||||
* @note Answer to application selection and PDOL
|
* @note Answer to application selection and PDOL
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue