[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
					
				| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich