[FL-2605] NFC new design (#1364)
* nfc: add new read scene * lib: refactore nfc library * mifare desfire: add read card fuction * lib nfc: add auto read worker * nfc: add supported cards * nfc: add mifare classic read success scene * nfc: add troyka support * submodule: update protobuf * nfc: mifare classic keys cache * nfc: rework mifare classic key cache * Correct spelling * nfc: add user dictionary * nfc: introduce block read map in fff * nfc: rework dict attack * nfc: improve dict attack * nfc: rework mifare classic format * nfc: rework MFC read with Reader * nfc: add gui for MFC read success scene * nfc: fix dict attack view gui * nfc: add retry and exit confirm scenes * nfc: add retry and exit scenes navigation * nfc: check user dictionary * nfc: remove unused scenes * nfc: rename functions in nfc worker * nfc: rename mf_classic_dict_attack -> dict_attack * nfc: change scenes names * nfc: remove scene tick events * nfc: rework dict calls with buffer streams * nfc: fix notifications * nfc: fix mf desfire navigation * nfc: remove notification from mf classic read success * nfc: fix read sectors calculation * nfc: add fallback for unknown card * nfc: show file name while emulating * nfc: fix build * nfc: fix memory leak * nfc: fix desfire read * nfc: add no dict found navigation * nfc: add read views * nfc: update card fix * nfc: fix access bytes save * nfc: add exit and retry confirm to mf ultralight read success * nfc: introduce detect reader * nfc: change record open arg to macros * nfc: fix start from archive Co-authored-by: Astra <astra@astrra.space> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									ec19c11dbe
								
							
						
					
					
						commit
						9c59bcd776
					
				
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -76,7 +76,7 @@ | |||||||
| /lib/microtar/ @skotopes @DrZlo13 @hedger | /lib/microtar/ @skotopes @DrZlo13 @hedger | ||||||
| /lib/mlib/ @skotopes @DrZlo13 @hedger | /lib/mlib/ @skotopes @DrZlo13 @hedger | ||||||
| /lib/nanopb/ @skotopes @DrZlo13 @hedger | /lib/nanopb/ @skotopes @DrZlo13 @hedger | ||||||
| /lib/nfc_protocols/ @skotopes @DrZlo13 @hedger @gornekich | /lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich | ||||||
| /lib/one_wire/ @skotopes @DrZlo13 @hedger | /lib/one_wire/ @skotopes @DrZlo13 @hedger | ||||||
| /lib/qrcode/ @skotopes @DrZlo13 @hedger | /lib/qrcode/ @skotopes @DrZlo13 @hedger | ||||||
| /lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm | /lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm | ||||||
|  | |||||||
| @ -9,5 +9,6 @@ enum NfcCustomEvent { | |||||||
|     NfcCustomEventByteInputDone, |     NfcCustomEventByteInputDone, | ||||||
|     NfcCustomEventTextInputDone, |     NfcCustomEventTextInputDone, | ||||||
|     NfcCustomEventDictAttackDone, |     NfcCustomEventDictAttackDone, | ||||||
|  |     NfcCustomEventDictAttackSkip, | ||||||
|     NfcCustomEventRpcLoad, |     NfcCustomEventRpcLoad, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -267,67 +267,67 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { | |||||||
| static const NfcGenerator mf_ul_generator = { | static const NfcGenerator mf_ul_generator = { | ||||||
|     .name = "Mifare Ultralight", |     .name = "Mifare Ultralight", | ||||||
|     .generator_func = nfc_generate_mf_ul_orig, |     .generator_func = nfc_generate_mf_ul_orig, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator mf_ul_11_generator = { | static const NfcGenerator mf_ul_11_generator = { | ||||||
|     .name = "Mifare Ultralight EV1 11", |     .name = "Mifare Ultralight EV1 11", | ||||||
|     .generator_func = nfc_generate_mf_ul_11, |     .generator_func = nfc_generate_mf_ul_11, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator mf_ul_h11_generator = { | static const NfcGenerator mf_ul_h11_generator = { | ||||||
|     .name = "Mifare Ultralight EV1 H11", |     .name = "Mifare Ultralight EV1 H11", | ||||||
|     .generator_func = nfc_generate_mf_ul_h11, |     .generator_func = nfc_generate_mf_ul_h11, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator mf_ul_21_generator = { | static const NfcGenerator mf_ul_21_generator = { | ||||||
|     .name = "Mifare Ultralight EV1 21", |     .name = "Mifare Ultralight EV1 21", | ||||||
|     .generator_func = nfc_generate_mf_ul_21, |     .generator_func = nfc_generate_mf_ul_21, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator mf_ul_h21_generator = { | static const NfcGenerator mf_ul_h21_generator = { | ||||||
|     .name = "Mifare Ultralight EV1 H21", |     .name = "Mifare Ultralight EV1 H21", | ||||||
|     .generator_func = nfc_generate_mf_ul_h21, |     .generator_func = nfc_generate_mf_ul_h21, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag203_generator = { | static const NfcGenerator ntag203_generator = { | ||||||
|     .name = "NTAG203", |     .name = "NTAG203", | ||||||
|     .generator_func = nfc_generate_mf_ul_ntag203, |     .generator_func = nfc_generate_mf_ul_ntag203, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag213_generator = { | static const NfcGenerator ntag213_generator = { | ||||||
|     .name = "NTAG213", |     .name = "NTAG213", | ||||||
|     .generator_func = nfc_generate_ntag213, |     .generator_func = nfc_generate_ntag213, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag215_generator = { | static const NfcGenerator ntag215_generator = { | ||||||
|     .name = "NTAG215", |     .name = "NTAG215", | ||||||
|     .generator_func = nfc_generate_ntag215, |     .generator_func = nfc_generate_ntag215, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag216_generator = { | static const NfcGenerator ntag216_generator = { | ||||||
|     .name = "NTAG216", |     .name = "NTAG216", | ||||||
|     .generator_func = nfc_generate_ntag216, |     .generator_func = nfc_generate_ntag216, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag_i2c_1k_generator = { | static const NfcGenerator ntag_i2c_1k_generator = { | ||||||
|     .name = "NTAG I2C 1k", |     .name = "NTAG I2C 1k", | ||||||
|     .generator_func = nfc_generate_ntag_i2c_1k, |     .generator_func = nfc_generate_ntag_i2c_1k, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag_i2c_2k_generator = { | static const NfcGenerator ntag_i2c_2k_generator = { | ||||||
|     .name = "NTAG I2C 2k", |     .name = "NTAG I2C 2k", | ||||||
|     .generator_func = nfc_generate_ntag_i2c_2k, |     .generator_func = nfc_generate_ntag_i2c_2k, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag_i2c_plus_1k_generator = { | static const NfcGenerator ntag_i2c_plus_1k_generator = { | ||||||
|     .name = "NTAG I2C Plus 1k", |     .name = "NTAG I2C Plus 1k", | ||||||
|     .generator_func = nfc_generate_ntag_i2c_plus_1k, |     .generator_func = nfc_generate_ntag_i2c_plus_1k, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| static const NfcGenerator ntag_i2c_plus_2k_generator = { | static const NfcGenerator ntag_i2c_plus_2k_generator = { | ||||||
|     .name = "NTAG I2C Plus 2k", |     .name = "NTAG I2C Plus 2k", | ||||||
|     .generator_func = nfc_generate_ntag_i2c_plus_2k, |     .generator_func = nfc_generate_ntag_i2c_plus_2k, | ||||||
|     .next_scene = NfcSceneMifareUlMenu}; |     .next_scene = NfcSceneMfUltralightMenu}; | ||||||
| 
 | 
 | ||||||
| const NfcGenerator* const nfc_generators[] = { | const NfcGenerator* const nfc_generators[] = { | ||||||
|     &mf_ul_generator, |     &mf_ul_generator, | ||||||
|  | |||||||
| @ -1,53 +0,0 @@ | |||||||
| #include "nfc_mf_classic_dict.h" |  | ||||||
| 
 |  | ||||||
| #include <flipper_format/flipper_format.h> |  | ||||||
| #include <lib/toolbox/args.h> |  | ||||||
| 
 |  | ||||||
| #define NFC_MF_CLASSIC_DICT_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") |  | ||||||
| 
 |  | ||||||
| #define NFC_MF_CLASSIC_KEY_LEN (13) |  | ||||||
| 
 |  | ||||||
| bool nfc_mf_classic_dict_check_presence(Storage* storage) { |  | ||||||
|     furi_assert(storage); |  | ||||||
|     return storage_common_stat(storage, NFC_MF_CLASSIC_DICT_PATH, NULL) == FSE_OK; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_mf_classic_dict_open_file(Stream* stream) { |  | ||||||
|     furi_assert(stream); |  | ||||||
|     return file_stream_open(stream, NFC_MF_CLASSIC_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_mf_classic_dict_close_file(Stream* stream) { |  | ||||||
|     furi_assert(stream); |  | ||||||
|     file_stream_close(stream); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_mf_classic_dict_get_next_key(Stream* stream, uint64_t* key) { |  | ||||||
|     furi_assert(stream); |  | ||||||
|     furi_assert(key); |  | ||||||
|     uint8_t key_byte_tmp = 0; |  | ||||||
|     string_t next_line; |  | ||||||
|     string_init(next_line); |  | ||||||
|     *key = 0; |  | ||||||
| 
 |  | ||||||
|     bool next_key_read = false; |  | ||||||
|     while(!next_key_read) { |  | ||||||
|         if(!stream_read_line(stream, next_line)) break; |  | ||||||
|         if(string_get_char(next_line, 0) == '#') continue; |  | ||||||
|         if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; |  | ||||||
|         for(uint8_t i = 0; i < 12; i += 2) { |  | ||||||
|             args_char_to_hex( |  | ||||||
|                 string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); |  | ||||||
|             *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); |  | ||||||
|         } |  | ||||||
|         next_key_read = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(next_line); |  | ||||||
|     return next_key_read; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_mf_classic_dict_reset(Stream* stream) { |  | ||||||
|     furi_assert(stream); |  | ||||||
|     stream_rewind(stream); |  | ||||||
| } |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <storage/storage.h> |  | ||||||
| #include <lib/toolbox/stream/file_stream.h> |  | ||||||
| 
 |  | ||||||
| bool nfc_mf_classic_dict_check_presence(Storage* storage); |  | ||||||
| 
 |  | ||||||
| bool nfc_mf_classic_dict_open_file(Stream* stream); |  | ||||||
| 
 |  | ||||||
| void nfc_mf_classic_dict_close_file(Stream* stream); |  | ||||||
| 
 |  | ||||||
| bool nfc_mf_classic_dict_get_next_key(Stream* stream, uint64_t* key); |  | ||||||
| 
 |  | ||||||
| void nfc_mf_classic_dict_reset(Stream* stream); |  | ||||||
| @ -13,12 +13,6 @@ bool nfc_back_event_callback(void* context) { | |||||||
|     return scene_manager_handle_back_event(nfc->scene_manager); |     return scene_manager_handle_back_event(nfc->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_tick_event_callback(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     scene_manager_handle_tick_event(nfc->scene_manager); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_rpc_exit_callback(Nfc* nfc) { | void nfc_rpc_exit_callback(Nfc* nfc) { | ||||||
|     if(nfc->rpc_state == NfcRpcStateEmulating) { |     if(nfc->rpc_state == NfcRpcStateEmulating) { | ||||||
|         // Stop worker
 |         // Stop worker
 | ||||||
| @ -36,11 +30,12 @@ void nfc_rpc_exit_callback(Nfc* nfc) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { | static bool nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { | ||||||
|     UNUSED(event); |     UNUSED(event); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     nfc->rpc_state = NfcRpcStateEmulated; |     nfc->rpc_state = NfcRpcStateEmulated; | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||||
| @ -67,20 +62,20 @@ static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, v | |||||||
|                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|                     nfc_worker_start( |                     nfc_worker_start( | ||||||
|                         nfc->worker, |                         nfc->worker, | ||||||
|                         NfcWorkerStateEmulateMifareUltralight, |                         NfcWorkerStateMfUltralightEmulate, | ||||||
|                         &nfc->dev->dev_data, |                         &nfc->dev->dev_data, | ||||||
|                         nfc_rpc_emulate_callback, |                         nfc_rpc_emulate_callback, | ||||||
|                         nfc); |                         nfc); | ||||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                     nfc_worker_start( |                     nfc_worker_start( | ||||||
|                         nfc->worker, |                         nfc->worker, | ||||||
|                         NfcWorkerStateEmulateMifareClassic, |                         NfcWorkerStateMfClassicEmulate, | ||||||
|                         &nfc->dev->dev_data, |                         &nfc->dev->dev_data, | ||||||
|                         nfc_rpc_emulate_callback, |                         nfc_rpc_emulate_callback, | ||||||
|                         nfc); |                         nfc); | ||||||
|                 } else { |                 } else { | ||||||
|                     nfc_worker_start( |                     nfc_worker_start( | ||||||
|                         nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc); |                         nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||||
|                 } |                 } | ||||||
|                 nfc->rpc_state = NfcRpcStateEmulating; |                 nfc->rpc_state = NfcRpcStateEmulating; | ||||||
|                 view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); |                 view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); | ||||||
| @ -102,7 +97,6 @@ Nfc* nfc_alloc() { | |||||||
|     view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc); |     view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc); | ||||||
|     view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback); |     view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback); | ||||||
|     view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback); |     view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback); | ||||||
|     view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100); |  | ||||||
| 
 | 
 | ||||||
|     // Nfc device
 |     // Nfc device
 | ||||||
|     nfc->dev = nfc_device_alloc(); |     nfc->dev = nfc_device_alloc(); | ||||||
| @ -155,7 +149,7 @@ Nfc* nfc_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card)); |         nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card)); | ||||||
| 
 | 
 | ||||||
|     // Dict Attack
 |     // Mifare Classic Dict Attack
 | ||||||
|     nfc->dict_attack = dict_attack_alloc(); |     nfc->dict_attack = dict_attack_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); |         nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); | ||||||
| @ -209,7 +203,7 @@ void nfc_free(Nfc* nfc) { | |||||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard); |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard); | ||||||
|     bank_card_free(nfc->bank_card); |     bank_card_free(nfc->bank_card); | ||||||
| 
 | 
 | ||||||
|     // Dict Attack
 |     // Mifare Classic Dict Attack
 | ||||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||||
|     dict_attack_free(nfc->dict_attack); |     dict_attack_free(nfc->dict_attack); | ||||||
| 
 | 
 | ||||||
| @ -301,9 +295,9 @@ int32_t nfc_app(void* p) { | |||||||
|                 nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); |                 nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); | ||||||
|             if(nfc_device_load(nfc->dev, p, true)) { |             if(nfc_device_load(nfc->dev, p, true)) { | ||||||
|                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|                 } else { |                 } else { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <cli/cli.h> | #include <cli/cli.h> | ||||||
| #include <toolbox/args.h> | #include <lib/toolbox/args.h> | ||||||
| 
 | 
 | ||||||
| #include "nfc_types.h" | #include <lib/nfc/nfc_types.h> | ||||||
|  | #include <lib/nfc/nfc_device.h> | ||||||
| 
 | 
 | ||||||
| static void nfc_cli_print_usage() { | static void nfc_cli_print_usage() { | ||||||
|     printf("Usage:\r\n"); |     printf("Usage:\r\n"); | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "nfc.h" | #include "nfc.h" | ||||||
| #include "nfc_types.h" |  | ||||||
| #include "nfc_worker.h" |  | ||||||
| #include "nfc_device.h" |  | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| @ -24,6 +21,11 @@ | |||||||
| #include <gui/modules/text_box.h> | #include <gui/modules/text_box.h> | ||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
| 
 | 
 | ||||||
|  | #include <lib/nfc/nfc_types.h> | ||||||
|  | #include <lib/nfc/nfc_worker.h> | ||||||
|  | #include <lib/nfc/nfc_device.h> | ||||||
|  | #include <lib/nfc/helpers/mf_classic_dict.h> | ||||||
|  | 
 | ||||||
| #include "views/bank_card.h" | #include "views/bank_card.h" | ||||||
| #include "views/dict_attack.h" | #include "views/dict_attack.h" | ||||||
| 
 | 
 | ||||||
| @ -32,8 +34,6 @@ | |||||||
| 
 | 
 | ||||||
| #include "rpc/rpc_app.h" | #include "rpc/rpc_app.h" | ||||||
| 
 | 
 | ||||||
| #define NFC_SEND_NOTIFICATION_FALSE (0UL) |  | ||||||
| #define NFC_SEND_NOTIFICATION_TRUE (1UL) |  | ||||||
| #define NFC_TEXT_STORE_SIZE 128 | #define NFC_TEXT_STORE_SIZE 128 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -56,6 +56,7 @@ struct Nfc { | |||||||
| 
 | 
 | ||||||
|     char text_store[NFC_TEXT_STORE_SIZE + 1]; |     char text_store[NFC_TEXT_STORE_SIZE + 1]; | ||||||
|     string_t text_box_store; |     string_t text_box_store; | ||||||
|  |     uint8_t byte_input_store[6]; | ||||||
| 
 | 
 | ||||||
|     void* rpc_ctx; |     void* rpc_ctx; | ||||||
|     NfcRpcState rpc_state; |     NfcRpcState rpc_state; | ||||||
|  | |||||||
| @ -1,708 +0,0 @@ | |||||||
| #include "nfc_worker_i.h" |  | ||||||
| #include <furi_hal.h> |  | ||||||
| 
 |  | ||||||
| #include <platform.h> |  | ||||||
| 
 |  | ||||||
| #define TAG "NfcWorker" |  | ||||||
| 
 |  | ||||||
| /***************************** NFC Worker API *******************************/ |  | ||||||
| 
 |  | ||||||
| NfcWorker* nfc_worker_alloc() { |  | ||||||
|     NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); |  | ||||||
| 
 |  | ||||||
|     // Worker thread attributes
 |  | ||||||
|     nfc_worker->thread = furi_thread_alloc(); |  | ||||||
|     furi_thread_set_name(nfc_worker->thread, "NfcWorker"); |  | ||||||
|     furi_thread_set_stack_size(nfc_worker->thread, 8192); |  | ||||||
|     furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); |  | ||||||
|     furi_thread_set_context(nfc_worker->thread, nfc_worker); |  | ||||||
| 
 |  | ||||||
|     nfc_worker->callback = NULL; |  | ||||||
|     nfc_worker->context = NULL; |  | ||||||
|     nfc_worker->storage = furi_record_open(RECORD_STORAGE); |  | ||||||
| 
 |  | ||||||
|     // Initialize rfal
 |  | ||||||
|     while(furi_hal_nfc_is_busy()) { |  | ||||||
|         furi_delay_ms(10); |  | ||||||
|     } |  | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); |  | ||||||
| 
 |  | ||||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { |  | ||||||
|         nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return nfc_worker; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_free(NfcWorker* nfc_worker) { |  | ||||||
|     furi_assert(nfc_worker); |  | ||||||
| 
 |  | ||||||
|     furi_thread_free(nfc_worker->thread); |  | ||||||
| 
 |  | ||||||
|     furi_record_close(RECORD_STORAGE); |  | ||||||
| 
 |  | ||||||
|     if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); |  | ||||||
| 
 |  | ||||||
|     free(nfc_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) { |  | ||||||
|     return nfc_worker->state; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_start( |  | ||||||
|     NfcWorker* nfc_worker, |  | ||||||
|     NfcWorkerState state, |  | ||||||
|     NfcDeviceData* dev_data, |  | ||||||
|     NfcWorkerCallback callback, |  | ||||||
|     void* context) { |  | ||||||
|     furi_assert(nfc_worker); |  | ||||||
|     furi_assert(dev_data); |  | ||||||
|     while(furi_hal_nfc_is_busy()) { |  | ||||||
|         furi_delay_ms(10); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     nfc_worker->callback = callback; |  | ||||||
|     nfc_worker->context = context; |  | ||||||
|     nfc_worker->dev_data = dev_data; |  | ||||||
|     nfc_worker_change_state(nfc_worker, state); |  | ||||||
|     furi_thread_start(nfc_worker->thread); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_stop(NfcWorker* nfc_worker) { |  | ||||||
|     furi_assert(nfc_worker); |  | ||||||
|     if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     furi_hal_nfc_stop(); |  | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); |  | ||||||
|     furi_thread_join(nfc_worker->thread); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { |  | ||||||
|     nfc_worker->state = state; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /***************************** NFC Worker Thread *******************************/ |  | ||||||
| 
 |  | ||||||
| int32_t nfc_worker_task(void* context) { |  | ||||||
|     NfcWorker* nfc_worker = context; |  | ||||||
| 
 |  | ||||||
|     furi_hal_nfc_exit_sleep(); |  | ||||||
| 
 |  | ||||||
|     if(nfc_worker->state == NfcWorkerStateDetect) { |  | ||||||
|         nfc_worker_detect(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulate) { |  | ||||||
|         nfc_worker_emulate(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateReadEMVApp) { |  | ||||||
|         nfc_worker_read_emv_app(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateReadEMVData) { |  | ||||||
|         nfc_worker_read_emv(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { |  | ||||||
|         nfc_worker_emulate_apdu(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareUltralight) { |  | ||||||
|         nfc_worker_read_mifare_ultralight(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { |  | ||||||
|         nfc_worker_emulate_mifare_ul(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { |  | ||||||
|         nfc_worker_mifare_classic_dict_attack(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { |  | ||||||
|         nfc_worker_emulate_mifare_classic(nfc_worker); |  | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { |  | ||||||
|         nfc_worker_read_mifare_desfire(nfc_worker); |  | ||||||
|     } |  | ||||||
|     furi_hal_nfc_sleep(); |  | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_detect(NfcWorker* nfc_worker) { |  | ||||||
|     nfc_device_data_clear(nfc_worker->dev_data); |  | ||||||
|     NfcDeviceData* dev_data = nfc_worker->dev_data; |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateDetect) { |  | ||||||
|         if(furi_hal_nfc_detect(nfc_data, 1000)) { |  | ||||||
|             // Process first found device
 |  | ||||||
|             if(nfc_data->type == FuriHalNfcTypeA) { |  | ||||||
|                 if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { |  | ||||||
|                     dev_data->protocol = NfcDeviceProtocolMifareUl; |  | ||||||
|                 } else if(mf_classic_check_card_type( |  | ||||||
|                               nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { |  | ||||||
|                     dev_data->protocol = NfcDeviceProtocolMifareClassic; |  | ||||||
|                 } else if(mf_df_check_card_type( |  | ||||||
|                               nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { |  | ||||||
|                     dev_data->protocol = NfcDeviceProtocolMifareDesfire; |  | ||||||
|                 } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { |  | ||||||
|                     dev_data->protocol = NfcDeviceProtocolEMV; |  | ||||||
|                 } else { |  | ||||||
|                     dev_data->protocol = NfcDeviceProtocolUnknown; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Notify caller and exit
 |  | ||||||
|             if(nfc_worker->callback) { |  | ||||||
|                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         furi_hal_nfc_sleep(); |  | ||||||
|         furi_delay_ms(100); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); |  | ||||||
|     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; |  | ||||||
|     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateEmulate) { |  | ||||||
|         if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) { |  | ||||||
|             if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { |  | ||||||
|                 reader_data->size = tx_rx.rx_bits / 8; |  | ||||||
|                 if(reader_data->size > 0) { |  | ||||||
|                     memcpy(reader_data->data, tx_rx.rx_data, reader_data->size); |  | ||||||
|                     if(nfc_worker->callback) { |  | ||||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 FURI_LOG_E(TAG, "Failed to get reader commands"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); |  | ||||||
|     EmvApplication emv_app = {}; |  | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
|     nfc_device_data_clear(result); |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { |  | ||||||
|         if(furi_hal_nfc_detect(nfc_data, 1000)) { |  | ||||||
|             // Card was found. Check that it supports EMV
 |  | ||||||
|             if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { |  | ||||||
|                 result->protocol = NfcDeviceProtocolEMV; |  | ||||||
|                 if(emv_search_application(&tx_rx, &emv_app)) { |  | ||||||
|                     // Notify caller and exit
 |  | ||||||
|                     result->emv_data.aid_len = emv_app.aid_len; |  | ||||||
|                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); |  | ||||||
|                     if(nfc_worker->callback) { |  | ||||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 FURI_LOG_W(TAG, "Card doesn't support EMV"); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             FURI_LOG_D(TAG, "Can't find any cards"); |  | ||||||
|         } |  | ||||||
|         furi_hal_nfc_sleep(); |  | ||||||
|         furi_delay_ms(20); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_emv(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); |  | ||||||
|     EmvApplication emv_app = {}; |  | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
|     nfc_device_data_clear(result); |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVData) { |  | ||||||
|         if(furi_hal_nfc_detect(nfc_data, 1000)) { |  | ||||||
|             // Card was found. Check that it supports EMV
 |  | ||||||
|             if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { |  | ||||||
|                 result->protocol = NfcDeviceProtocolEMV; |  | ||||||
|                 if(emv_read_bank_card(&tx_rx, &emv_app)) { |  | ||||||
|                     result->emv_data.number_len = emv_app.card_number_len; |  | ||||||
|                     memcpy( |  | ||||||
|                         result->emv_data.number, emv_app.card_number, result->emv_data.number_len); |  | ||||||
|                     result->emv_data.aid_len = emv_app.aid_len; |  | ||||||
|                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); |  | ||||||
|                     if(emv_app.name_found) { |  | ||||||
|                         memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name)); |  | ||||||
|                     } |  | ||||||
|                     if(emv_app.exp_month) { |  | ||||||
|                         result->emv_data.exp_mon = emv_app.exp_month; |  | ||||||
|                         result->emv_data.exp_year = emv_app.exp_year; |  | ||||||
|                     } |  | ||||||
|                     if(emv_app.country_code) { |  | ||||||
|                         result->emv_data.country_code = emv_app.country_code; |  | ||||||
|                     } |  | ||||||
|                     if(emv_app.currency_code) { |  | ||||||
|                         result->emv_data.currency_code = emv_app.currency_code; |  | ||||||
|                     } |  | ||||||
|                     // Notify caller and exit
 |  | ||||||
|                     if(nfc_worker->callback) { |  | ||||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 FURI_LOG_W(TAG, "Card doesn't support EMV"); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             FURI_LOG_D(TAG, "Can't find any cards"); |  | ||||||
|         } |  | ||||||
|         furi_hal_nfc_sleep(); |  | ||||||
|         furi_delay_ms(20); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); |  | ||||||
|     FuriHalNfcDevData params = { |  | ||||||
|         .uid = {0xCF, 0x72, 0xd4, 0x40}, |  | ||||||
|         .uid_len = 4, |  | ||||||
|         .atqa = {0x00, 0x04}, |  | ||||||
|         .sak = 0x20, |  | ||||||
|         .type = FuriHalNfcTypeA, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateEmulateApdu) { |  | ||||||
|         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { |  | ||||||
|             FURI_LOG_D(TAG, "POS terminal detected"); |  | ||||||
|             if(emv_card_emulation(&tx_rx)) { |  | ||||||
|                 FURI_LOG_D(TAG, "EMV card emulated"); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             FURI_LOG_D(TAG, "Can't find reader"); |  | ||||||
|         } |  | ||||||
|         furi_hal_nfc_sleep(); |  | ||||||
|         furi_delay_ms(20); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); |  | ||||||
|     MfUltralightReader reader = {}; |  | ||||||
|     MfUltralightData data = {}; |  | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareUltralight) { |  | ||||||
|         if(furi_hal_nfc_detect(nfc_data, 300)) { |  | ||||||
|             if(nfc_data->type == FuriHalNfcTypeA && |  | ||||||
|                mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { |  | ||||||
|                 FURI_LOG_D(TAG, "Found Mifare Ultralight tag. Start reading"); |  | ||||||
|                 if(mf_ul_read_card(&tx_rx, &reader, &data)) { |  | ||||||
|                     result->protocol = NfcDeviceProtocolMifareUl; |  | ||||||
|                     result->mf_ul_data = data; |  | ||||||
|                     // Notify caller and exit
 |  | ||||||
|                     if(nfc_worker->callback) { |  | ||||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 } else { |  | ||||||
|                     FURI_LOG_D(TAG, "Failed reading Mifare Ultralight"); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 FURI_LOG_W(TAG, "Tag is not Mifare Ultralight"); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             FURI_LOG_D(TAG, "Can't find any tags"); |  | ||||||
|         } |  | ||||||
|         furi_hal_nfc_sleep(); |  | ||||||
|         furi_delay_ms(100); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
|     MfUltralightEmulator emulator = {}; |  | ||||||
|     mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { |  | ||||||
|         mf_ul_reset_emulation(&emulator, true); |  | ||||||
|         furi_hal_nfc_emulate_nfca( |  | ||||||
|             nfc_data->uid, |  | ||||||
|             nfc_data->uid_len, |  | ||||||
|             nfc_data->atqa, |  | ||||||
|             nfc_data->sak, |  | ||||||
|             mf_ul_prepare_emulation_response, |  | ||||||
|             &emulator, |  | ||||||
|             5000); |  | ||||||
|         // Check if data was modified
 |  | ||||||
|         if(emulator.data_changed) { |  | ||||||
|             nfc_worker->dev_data->mf_ul_data = emulator.data; |  | ||||||
|             if(nfc_worker->callback) { |  | ||||||
|                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|             } |  | ||||||
|             emulator.data_changed = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { |  | ||||||
|     furi_assert(nfc_worker->callback); |  | ||||||
|     FuriHalNfcTxRxContext tx_rx_ctx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx_ctx, false); |  | ||||||
|     MfClassicAuthContext auth_ctx = {}; |  | ||||||
|     MfClassicReader reader = {}; |  | ||||||
|     uint64_t curr_key = 0; |  | ||||||
|     uint16_t curr_sector = 0; |  | ||||||
|     uint8_t total_sectors = 0; |  | ||||||
|     NfcWorkerEvent event; |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
| 
 |  | ||||||
|     // Open dictionary
 |  | ||||||
|     nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage); |  | ||||||
|     if(!nfc_mf_classic_dict_open_file(nfc_worker->dict_stream)) { |  | ||||||
|         event = NfcWorkerEventNoDictFound; |  | ||||||
|         nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|         nfc_mf_classic_dict_close_file(nfc_worker->dict_stream); |  | ||||||
|         stream_free(nfc_worker->dict_stream); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Detect Mifare Classic card
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareClassic) { |  | ||||||
|         if(furi_hal_nfc_detect(nfc_data, 300)) { |  | ||||||
|             if(mf_classic_get_type( |  | ||||||
|                    nfc_data->uid, |  | ||||||
|                    nfc_data->uid_len, |  | ||||||
|                    nfc_data->atqa[0], |  | ||||||
|                    nfc_data->atqa[1], |  | ||||||
|                    nfc_data->sak, |  | ||||||
|                    &reader)) { |  | ||||||
|                 total_sectors = mf_classic_get_total_sectors_num(&reader); |  | ||||||
|                 if(reader.type == MfClassicType1k) { |  | ||||||
|                     event = NfcWorkerEventDetectedClassic1k; |  | ||||||
|                 } else { |  | ||||||
|                     event = NfcWorkerEventDetectedClassic4k; |  | ||||||
|                 } |  | ||||||
|                 nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             event = NfcWorkerEventNoCardDetected; |  | ||||||
|             nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { |  | ||||||
|         bool card_removed_notified = false; |  | ||||||
|         bool card_found_notified = false; |  | ||||||
|         // Seek for mifare classic keys
 |  | ||||||
|         for(curr_sector = 0; curr_sector < total_sectors; curr_sector++) { |  | ||||||
|             FURI_LOG_I(TAG, "Sector: %d ...", curr_sector); |  | ||||||
|             event = NfcWorkerEventNewSector; |  | ||||||
|             nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|             mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector); |  | ||||||
|             bool sector_key_found = false; |  | ||||||
|             while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) { |  | ||||||
|                 furi_hal_nfc_sleep(); |  | ||||||
|                 if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) { |  | ||||||
|                     if(!card_found_notified) { |  | ||||||
|                         if(reader.type == MfClassicType1k) { |  | ||||||
|                             event = NfcWorkerEventDetectedClassic1k; |  | ||||||
|                         } else { |  | ||||||
|                             event = NfcWorkerEventDetectedClassic4k; |  | ||||||
|                         } |  | ||||||
|                         nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|                         card_found_notified = true; |  | ||||||
|                         card_removed_notified = false; |  | ||||||
|                     } |  | ||||||
|                     FURI_LOG_D( |  | ||||||
|                         TAG, |  | ||||||
|                         "Try to auth to sector %d with key %04lx%08lx", |  | ||||||
|                         curr_sector, |  | ||||||
|                         (uint32_t)(curr_key >> 32), |  | ||||||
|                         (uint32_t)curr_key); |  | ||||||
|                     if(mf_classic_auth_attempt(&tx_rx_ctx, &auth_ctx, curr_key)) { |  | ||||||
|                         sector_key_found = true; |  | ||||||
|                         if((auth_ctx.key_a != MF_CLASSIC_NO_KEY) && |  | ||||||
|                            (auth_ctx.key_b != MF_CLASSIC_NO_KEY)) |  | ||||||
|                             break; |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     // Notify that no tag is availalble
 |  | ||||||
|                     FURI_LOG_D(TAG, "Can't find tags"); |  | ||||||
|                     if(!card_removed_notified) { |  | ||||||
|                         event = NfcWorkerEventNoCardDetected; |  | ||||||
|                         nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|                         card_removed_notified = true; |  | ||||||
|                         card_found_notified = false; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break; |  | ||||||
|                 furi_delay_tick(1); |  | ||||||
|             } |  | ||||||
|             if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break; |  | ||||||
|             if(sector_key_found) { |  | ||||||
|                 // Notify that keys were found
 |  | ||||||
|                 if(auth_ctx.key_a != MF_CLASSIC_NO_KEY) { |  | ||||||
|                     FURI_LOG_I( |  | ||||||
|                         TAG, |  | ||||||
|                         "Sector %d key A: %04lx%08lx", |  | ||||||
|                         curr_sector, |  | ||||||
|                         (uint32_t)(auth_ctx.key_a >> 32), |  | ||||||
|                         (uint32_t)auth_ctx.key_a); |  | ||||||
|                     event = NfcWorkerEventFoundKeyA; |  | ||||||
|                     nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|                 } |  | ||||||
|                 if(auth_ctx.key_b != MF_CLASSIC_NO_KEY) { |  | ||||||
|                     FURI_LOG_I( |  | ||||||
|                         TAG, |  | ||||||
|                         "Sector %d key B: %04lx%08lx", |  | ||||||
|                         curr_sector, |  | ||||||
|                         (uint32_t)(auth_ctx.key_b >> 32), |  | ||||||
|                         (uint32_t)auth_ctx.key_b); |  | ||||||
|                     event = NfcWorkerEventFoundKeyB; |  | ||||||
|                     nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|                 } |  | ||||||
|                 // Add sectors to read sequence
 |  | ||||||
|                 mf_classic_reader_add_sector(&reader, curr_sector, auth_ctx.key_a, auth_ctx.key_b); |  | ||||||
|             } |  | ||||||
|             nfc_mf_classic_dict_reset(nfc_worker->dict_stream); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { |  | ||||||
|         FURI_LOG_I(TAG, "Found keys to %d sectors. Start reading sectors", reader.sectors_to_read); |  | ||||||
|         uint8_t sectors_read = |  | ||||||
|             mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data); |  | ||||||
|         if(sectors_read) { |  | ||||||
|             event = NfcWorkerEventSuccess; |  | ||||||
|             nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; |  | ||||||
|             FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read); |  | ||||||
|         } else { |  | ||||||
|             event = NfcWorkerEventFail; |  | ||||||
|             FURI_LOG_W(TAG, "Failed to read any sector"); |  | ||||||
|         } |  | ||||||
|         nfc_worker->callback(event, nfc_worker->context); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     nfc_mf_classic_dict_close_file(nfc_worker->dict_stream); |  | ||||||
|     stream_free(nfc_worker->dict_stream); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
|     MfClassicEmulator emulator = { |  | ||||||
|         .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), |  | ||||||
|         .data = nfc_worker->dev_data->mf_classic_data, |  | ||||||
|         .data_changed = false, |  | ||||||
|     }; |  | ||||||
|     NfcaSignal* nfca_signal = nfca_signal_alloc(); |  | ||||||
|     tx_rx.nfca_signal = nfca_signal; |  | ||||||
| 
 |  | ||||||
|     rfal_platform_spi_acquire(); |  | ||||||
| 
 |  | ||||||
|     furi_hal_nfc_listen_start(nfc_data); |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { |  | ||||||
|         if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { |  | ||||||
|             mf_classic_emulator(&emulator, &tx_rx); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     if(emulator.data_changed) { |  | ||||||
|         nfc_worker->dev_data->mf_classic_data = emulator.data; |  | ||||||
|         if(nfc_worker->callback) { |  | ||||||
|             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|         } |  | ||||||
|         emulator.data_changed = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     nfca_signal_free(nfca_signal); |  | ||||||
| 
 |  | ||||||
|     rfal_platform_spi_release(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { |  | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |  | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); |  | ||||||
|     NfcDeviceData* result = nfc_worker->dev_data; |  | ||||||
|     nfc_device_data_clear(result); |  | ||||||
|     MifareDesfireData* data = &result->mf_df_data; |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |  | ||||||
| 
 |  | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { |  | ||||||
|         furi_hal_nfc_sleep(); |  | ||||||
|         if(!furi_hal_nfc_detect(nfc_data, 300)) { |  | ||||||
|             furi_delay_ms(100); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         memset(data, 0, sizeof(MifareDesfireData)); |  | ||||||
|         if(nfc_data->type != FuriHalNfcTypeA || |  | ||||||
|            !mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { |  | ||||||
|             FURI_LOG_D(TAG, "Tag is not DESFire"); |  | ||||||
|             furi_delay_ms(100); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         FURI_LOG_D(TAG, "Found DESFire tag"); |  | ||||||
| 
 |  | ||||||
|         result->protocol = NfcDeviceProtocolMifareDesfire; |  | ||||||
| 
 |  | ||||||
|         // Get DESFire version
 |  | ||||||
|         tx_rx.tx_bits = 8 * mf_df_prepare_get_version(tx_rx.tx_data); |  | ||||||
|         if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|             FURI_LOG_W(TAG, "Bad exchange getting version"); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         if(!mf_df_parse_get_version_response(tx_rx.rx_data, tx_rx.rx_bits / 8, &data->version)) { |  | ||||||
|             FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response"); |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         tx_rx.tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx.tx_data); |  | ||||||
|         if(furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); |  | ||||||
|             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); |  | ||||||
|             if(!mf_df_parse_get_free_memory_response( |  | ||||||
|                    tx_rx.rx_data, tx_rx.rx_bits / 8, data->free_memory)) { |  | ||||||
|                 FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); |  | ||||||
|                 free(data->free_memory); |  | ||||||
|                 data->free_memory = NULL; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data); |  | ||||||
|         if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|             FURI_LOG_D(TAG, "Bad exchange getting key settings"); |  | ||||||
|         } else { |  | ||||||
|             data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); |  | ||||||
|             memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); |  | ||||||
|             if(!mf_df_parse_get_key_settings_response( |  | ||||||
|                    tx_rx.rx_data, tx_rx.rx_bits / 8, data->master_key_settings)) { |  | ||||||
|                 FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); |  | ||||||
|                 free(data->master_key_settings); |  | ||||||
|                 data->master_key_settings = NULL; |  | ||||||
|             } else { |  | ||||||
|                 MifareDesfireKeyVersion** key_version_head = |  | ||||||
|                     &data->master_key_settings->key_version_head; |  | ||||||
|                 for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { |  | ||||||
|                     tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id); |  | ||||||
|                     if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|                         FURI_LOG_W(TAG, "Bad exchange getting key version"); |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); |  | ||||||
|                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); |  | ||||||
|                     key_version->id = key_id; |  | ||||||
|                     if(!mf_df_parse_get_key_version_response( |  | ||||||
|                            tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) { |  | ||||||
|                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); |  | ||||||
|                         free(key_version); |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     *key_version_head = key_version; |  | ||||||
|                     key_version_head = &key_version->next; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         tx_rx.tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx.tx_data); |  | ||||||
|         if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|             FURI_LOG_W(TAG, "Bad exchange getting application IDs"); |  | ||||||
|         } else { |  | ||||||
|             if(!mf_df_parse_get_application_ids_response( |  | ||||||
|                    tx_rx.rx_data, tx_rx.rx_bits / 8, &data->app_head)) { |  | ||||||
|                 FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { |  | ||||||
|             tx_rx.tx_bits = 8 * mf_df_prepare_select_application(tx_rx.tx_data, app->id); |  | ||||||
|             if(!furi_hal_nfc_tx_rx_full(&tx_rx) || |  | ||||||
|                !mf_df_parse_select_application_response(tx_rx.rx_data, tx_rx.rx_bits / 8)) { |  | ||||||
|                 FURI_LOG_W(TAG, "Bad exchange selecting application"); |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data); |  | ||||||
|             if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|                 FURI_LOG_W(TAG, "Bad exchange getting key settings"); |  | ||||||
|             } else { |  | ||||||
|                 app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); |  | ||||||
|                 memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); |  | ||||||
|                 if(!mf_df_parse_get_key_settings_response( |  | ||||||
|                        tx_rx.rx_data, tx_rx.rx_bits / 8, app->key_settings)) { |  | ||||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); |  | ||||||
|                     free(app->key_settings); |  | ||||||
|                     app->key_settings = NULL; |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; |  | ||||||
|                 for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { |  | ||||||
|                     tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id); |  | ||||||
|                     if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|                         FURI_LOG_W(TAG, "Bad exchange getting key version"); |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); |  | ||||||
|                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); |  | ||||||
|                     key_version->id = key_id; |  | ||||||
|                     if(!mf_df_parse_get_key_version_response( |  | ||||||
|                            tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) { |  | ||||||
|                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); |  | ||||||
|                         free(key_version); |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     *key_version_head = key_version; |  | ||||||
|                     key_version_head = &key_version->next; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             tx_rx.tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx.tx_data); |  | ||||||
|             if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|                 FURI_LOG_W(TAG, "Bad exchange getting file IDs"); |  | ||||||
|             } else { |  | ||||||
|                 if(!mf_df_parse_get_file_ids_response( |  | ||||||
|                        tx_rx.rx_data, tx_rx.rx_bits / 8, &app->file_head)) { |  | ||||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { |  | ||||||
|                 tx_rx.tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx.tx_data, file->id); |  | ||||||
|                 if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|                     FURI_LOG_W(TAG, "Bad exchange getting file settings"); |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 if(!mf_df_parse_get_file_settings_response( |  | ||||||
|                        tx_rx.rx_data, tx_rx.rx_bits / 8, file)) { |  | ||||||
|                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 switch(file->type) { |  | ||||||
|                 case MifareDesfireFileTypeStandard: |  | ||||||
|                 case MifareDesfireFileTypeBackup: |  | ||||||
|                     tx_rx.tx_bits = 8 * mf_df_prepare_read_data(tx_rx.tx_data, file->id, 0, 0); |  | ||||||
|                     break; |  | ||||||
|                 case MifareDesfireFileTypeValue: |  | ||||||
|                     tx_rx.tx_bits = 8 * mf_df_prepare_get_value(tx_rx.tx_data, file->id); |  | ||||||
|                     break; |  | ||||||
|                 case MifareDesfireFileTypeLinearRecord: |  | ||||||
|                 case MifareDesfireFileTypeCyclicRecord: |  | ||||||
|                     tx_rx.tx_bits = 8 * mf_df_prepare_read_records(tx_rx.tx_data, file->id, 0, 0); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { |  | ||||||
|                     FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id); |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 if(!mf_df_parse_read_data_response(tx_rx.rx_data, tx_rx.rx_bits / 8, file)) { |  | ||||||
|                     FURI_LOG_W(TAG, "Bad response reading file %d", file->id); |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Notify caller and exit
 |  | ||||||
|         if(nfc_worker->callback) { |  | ||||||
|             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,57 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "nfc_worker.h" |  | ||||||
| #include "nfc_i.h" |  | ||||||
| 
 |  | ||||||
| #include <furi.h> |  | ||||||
| #include <lib/toolbox/stream/file_stream.h> |  | ||||||
| 
 |  | ||||||
| #include <lib/nfc_protocols/nfc_util.h> |  | ||||||
| #include <lib/nfc_protocols/emv.h> |  | ||||||
| #include <lib/nfc_protocols/mifare_common.h> |  | ||||||
| #include <lib/nfc_protocols/mifare_ultralight.h> |  | ||||||
| #include <lib/nfc_protocols/mifare_classic.h> |  | ||||||
| #include <lib/nfc_protocols/mifare_desfire.h> |  | ||||||
| #include <lib/nfc_protocols/nfca.h> |  | ||||||
| 
 |  | ||||||
| #include "helpers/nfc_mf_classic_dict.h" |  | ||||||
| #include "helpers/nfc_debug_pcap.h" |  | ||||||
| 
 |  | ||||||
| struct NfcWorker { |  | ||||||
|     FuriThread* thread; |  | ||||||
|     Storage* storage; |  | ||||||
|     Stream* dict_stream; |  | ||||||
| 
 |  | ||||||
|     NfcDeviceData* dev_data; |  | ||||||
| 
 |  | ||||||
|     NfcWorkerCallback callback; |  | ||||||
|     void* context; |  | ||||||
| 
 |  | ||||||
|     NfcWorkerState state; |  | ||||||
| 
 |  | ||||||
|     NfcDebugPcapWorker* debug_pcap_worker; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); |  | ||||||
| 
 |  | ||||||
| int32_t nfc_worker_task(void* context); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_emv_app(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_emv(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_detect(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker); |  | ||||||
| 
 |  | ||||||
| void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker); |  | ||||||
| @ -1,90 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| 
 |  | ||||||
| enum SubmenuIndex { |  | ||||||
|     SubmenuIndexRunApp, |  | ||||||
|     SubmenuIndexChooseScript, |  | ||||||
|     SubmenuIndexEmulate, |  | ||||||
|     SubmenuIndexSave, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void nfc_scene_card_menu_submenu_callback(void* context, uint32_t index) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_card_menu_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     Submenu* submenu = nfc->submenu; |  | ||||||
| 
 |  | ||||||
|     if(nfc->dev->dev_data.protocol > NfcDeviceProtocolUnknown) { |  | ||||||
|         submenu_add_item( |  | ||||||
|             submenu, |  | ||||||
|             "Run Compatible App", |  | ||||||
|             SubmenuIndexRunApp, |  | ||||||
|             nfc_scene_card_menu_submenu_callback, |  | ||||||
|             nfc); |  | ||||||
|     } |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, |  | ||||||
|         "Additional reading scripts", |  | ||||||
|         SubmenuIndexChooseScript, |  | ||||||
|         nfc_scene_card_menu_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, "Emulate UID", SubmenuIndexEmulate, nfc_scene_card_menu_submenu_callback, nfc); |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, "Save UID", SubmenuIndexSave, nfc_scene_card_menu_submenu_callback, nfc); |  | ||||||
|     submenu_set_selected_item( |  | ||||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneCardMenu)); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == SubmenuIndexRunApp) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); |  | ||||||
|             if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { |  | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); |  | ||||||
|             } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareDesfire) { |  | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); |  | ||||||
|             } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolEMV) { |  | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); |  | ||||||
|             } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { |  | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); |  | ||||||
|             } |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == SubmenuIndexChooseScript) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexChooseScript); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == SubmenuIndexEmulate) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexEmulate); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == SubmenuIndexSave) { |  | ||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave); |  | ||||||
|             nfc->dev->format = NfcDeviceSaveFormatUid; |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |  | ||||||
|         consumed = |  | ||||||
|             scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_card_menu_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     submenu_reset(nfc->submenu); |  | ||||||
| } |  | ||||||
| @ -1,41 +1,40 @@ | |||||||
| ADD_SCENE(nfc, start, Start) | ADD_SCENE(nfc, start, Start) | ||||||
| ADD_SCENE(nfc, read_card, ReadCard) | ADD_SCENE(nfc, read, Read) | ||||||
| ADD_SCENE(nfc, read_card_success, ReadCardSuccess) |  | ||||||
| ADD_SCENE(nfc, card_menu, CardMenu) |  | ||||||
| ADD_SCENE(nfc, emulate_uid, EmulateUid) |  | ||||||
| ADD_SCENE(nfc, save_name, SaveName) |  | ||||||
| ADD_SCENE(nfc, save_success, SaveSuccess) |  | ||||||
| ADD_SCENE(nfc, file_select, FileSelect) |  | ||||||
| ADD_SCENE(nfc, saved_menu, SavedMenu) | ADD_SCENE(nfc, saved_menu, SavedMenu) | ||||||
|  | ADD_SCENE(nfc, extra_actions, ExtraActions) | ||||||
| ADD_SCENE(nfc, set_type, SetType) | ADD_SCENE(nfc, set_type, SetType) | ||||||
| ADD_SCENE(nfc, set_sak, SetSak) | ADD_SCENE(nfc, set_sak, SetSak) | ||||||
| ADD_SCENE(nfc, set_atqa, SetAtqua) | ADD_SCENE(nfc, set_atqa, SetAtqua) | ||||||
| ADD_SCENE(nfc, set_uid, SetUid) | ADD_SCENE(nfc, set_uid, SetUid) | ||||||
| ADD_SCENE(nfc, scripts_menu, ScriptsMenu) | ADD_SCENE(nfc, generate_info, GenerateInfo) | ||||||
| ADD_SCENE(nfc, read_mifare_ul, ReadMifareUl) | ADD_SCENE(nfc, read_card_success, ReadCardSuccess) | ||||||
| ADD_SCENE(nfc, read_mifare_ul_success, ReadMifareUlSuccess) | ADD_SCENE(nfc, save_name, SaveName) | ||||||
| ADD_SCENE(nfc, mifare_ul_menu, MifareUlMenu) | ADD_SCENE(nfc, save_success, SaveSuccess) | ||||||
| ADD_SCENE(nfc, emulate_mifare_ul, EmulateMifareUl) | ADD_SCENE(nfc, file_select, FileSelect) | ||||||
| ADD_SCENE(nfc, read_emv_app, ReadEmvApp) | ADD_SCENE(nfc, emulate_uid, EmulateUid) | ||||||
| ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess) | ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) | ||||||
| ADD_SCENE(nfc, read_mifare_desfire, ReadMifareDesfire) | ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) | ||||||
| ADD_SCENE(nfc, read_mifare_desfire_success, ReadMifareDesfireSuccess) | ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) | ||||||
| ADD_SCENE(nfc, mifare_desfire_menu, MifareDesfireMenu) | ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) | ||||||
| ADD_SCENE(nfc, mifare_desfire_data, MifareDesfireData) | ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) | ||||||
| ADD_SCENE(nfc, mifare_desfire_app, MifareDesfireApp) | ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) | ||||||
|  | ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) | ||||||
|  | ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) | ||||||
|  | ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) | ||||||
|  | ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) | ||||||
|  | ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) | ||||||
|  | ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) | ||||||
|  | ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) | ||||||
|  | ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) | ||||||
|  | ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) | ||||||
| ADD_SCENE(nfc, device_info, DeviceInfo) | ADD_SCENE(nfc, device_info, DeviceInfo) | ||||||
| ADD_SCENE(nfc, delete, Delete) | ADD_SCENE(nfc, delete, Delete) | ||||||
| ADD_SCENE(nfc, delete_success, DeleteSuccess) | ADD_SCENE(nfc, delete_success, DeleteSuccess) | ||||||
| ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm) |  | ||||||
| ADD_SCENE(nfc, read_emv_data, ReadEmvData) |  | ||||||
| ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess) |  | ||||||
| ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) |  | ||||||
| ADD_SCENE(nfc, restore_original, RestoreOriginal) | ADD_SCENE(nfc, restore_original, RestoreOriginal) | ||||||
| ADD_SCENE(nfc, debug, Debug) | ADD_SCENE(nfc, debug, Debug) | ||||||
| ADD_SCENE(nfc, field, Field) | ADD_SCENE(nfc, field, Field) | ||||||
| ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) |  | ||||||
| ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) |  | ||||||
| ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) |  | ||||||
| ADD_SCENE(nfc, dict_not_found, DictNotFound) | ADD_SCENE(nfc, dict_not_found, DictNotFound) | ||||||
| ADD_SCENE(nfc, rpc, Rpc) | ADD_SCENE(nfc, rpc, Rpc) | ||||||
| ADD_SCENE(nfc, generate_info, GenerateInfo) | ADD_SCENE(nfc, exit_confirm, ExitConfirm) | ||||||
|  | ADD_SCENE(nfc, retry_confirm, RetryConfirm) | ||||||
|  | ADD_SCENE(nfc, detect_reader, DetectReader) | ||||||
|  | |||||||
							
								
								
									
										143
									
								
								applications/nfc/scenes/nfc_scene_detect_reader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								applications/nfc/scenes/nfc_scene_detect_reader.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | #define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200) | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     NfcSceneDetectReaderStateWidget, | ||||||
|  |     NfcSceneDetectReaderStateTextBox, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     UNUSED(event); | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_detect_reader_textbox_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Add widget with device name or inform that data received
 | ||||||
|  | static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) { | ||||||
|  |     Widget* widget = nfc->widget; | ||||||
|  |     widget_reset(widget); | ||||||
|  | 
 | ||||||
|  |     widget_add_icon_element(widget, 0, 14, &I_Reader_detect); | ||||||
|  |     widget_add_string_element( | ||||||
|  |         widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold near reader"); | ||||||
|  |     widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); | ||||||
|  | 
 | ||||||
|  |     if(data_received) { | ||||||
|  |         widget_add_button_element( | ||||||
|  |             widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_detect_reader_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||||
|  |     FuriHalNfcDevData nfc_params = { | ||||||
|  |         .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, | ||||||
|  |         .uid_len = 7, | ||||||
|  |         .atqa = {0x44, 0x00}, | ||||||
|  |         .sak = 0x08, | ||||||
|  |         .type = FuriHalNfcTypeA, | ||||||
|  |     }; | ||||||
|  |     nfc->dev->dev_data.nfc_data = nfc_params; | ||||||
|  | 
 | ||||||
|  |     // Setup Widget
 | ||||||
|  |     nfc_scene_detect_reader_widget_config(nfc, false); | ||||||
|  |     // Setup TextBox
 | ||||||
|  |     TextBox* text_box = nfc->text_box; | ||||||
|  |     text_box_set_font(text_box, TextBoxFontHex); | ||||||
|  |     text_box_set_focus(text_box, TextBoxFocusEnd); | ||||||
|  |     string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     // Set Widget state and view
 | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |     // Start worker
 | ||||||
|  |     memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, | ||||||
|  |         NfcWorkerStateUidEmulate, | ||||||
|  |         &nfc->dev->dev_data, | ||||||
|  |         nfc_detect_reader_worker_callback, | ||||||
|  |         nfc); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_start(nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcCustomEventWorkerExit) { | ||||||
|  |             // Add data button to widget if data is received for the first time
 | ||||||
|  |             if(!string_size(nfc->text_box_store)) { | ||||||
|  |                 nfc_scene_detect_reader_widget_config(nfc, true); | ||||||
|  |             } | ||||||
|  |             // Update TextBox data
 | ||||||
|  |             if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) { | ||||||
|  |                 string_cat_printf(nfc->text_box_store, "R:"); | ||||||
|  |                 for(uint16_t i = 0; i < reader_data->size; i++) { | ||||||
|  |                     string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); | ||||||
|  |                 } | ||||||
|  |                 string_push_back(nfc->text_box_store, '\n'); | ||||||
|  |                 text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); | ||||||
|  |             } | ||||||
|  |             memset(reader_data, 0, sizeof(NfcReaderRequestData)); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         if(state == NfcSceneDetectReaderStateTextBox) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_detect_reader_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  |     text_box_reset(nfc->text_box); | ||||||
|  |     string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_stop(nfc); | ||||||
|  | } | ||||||
| @ -190,7 +190,7 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); |                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { |             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|         } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { |         } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { | ||||||
|  | |||||||
| @ -31,12 +31,9 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == NfcCustomEventViewExit) { |         if(event.event == NfcCustomEventViewExit) { | ||||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneScriptsMenu)) { |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     nfc->scene_manager, NfcSceneScriptsMenu); |                     nfc->scene_manager, NfcSceneExtraActions); | ||||||
|             } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) { |  | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |  | ||||||
|                     nfc->scene_manager, NfcSceneCardMenu); |  | ||||||
|             } else { |             } else { | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     nfc->scene_manager, NfcSceneStart); |                     nfc->scene_manager, NfcSceneStart); | ||||||
|  | |||||||
| @ -8,11 +8,12 @@ enum { | |||||||
|     NfcSceneEmulateUidStateTextBox, |     NfcSceneEmulateUidStateTextBox, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { | bool nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|     UNUSED(event); |     UNUSED(event); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_emulate_uid_widget_callback(GuiButtonType result, InputType type, void* context) { | void nfc_scene_emulate_uid_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
| @ -76,7 +77,7 @@ void nfc_scene_emulate_uid_on_enter(void* context) { | |||||||
|     memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); |     memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); | ||||||
|     nfc_worker_start( |     nfc_worker_start( | ||||||
|         nfc->worker, |         nfc->worker, | ||||||
|         NfcWorkerStateEmulate, |         NfcWorkerStateUidEmulate, | ||||||
|         &nfc->dev->dev_data, |         &nfc->dev->dev_data, | ||||||
|         nfc_emulate_uid_worker_callback, |         nfc_emulate_uid_worker_callback, | ||||||
|         nfc); |         nfc); | ||||||
| @ -90,9 +91,7 @@ bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) { | |||||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateUid); |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeTick) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         consumed = true; |  | ||||||
|     } else if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |         if(event.event == NfcCustomEventWorkerExit) { | ||||||
|             // Add data button to widget if data is received for the first time
 |             // Add data button to widget if data is received for the first time
 | ||||||
|             if(!string_size(nfc->text_box_store)) { |             if(!string_size(nfc->text_box_store)) { | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								applications/nfc/scenes/nfc_scene_read_emv_data_success.c → applications/nfc/scenes/nfc_scene_emv_read_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										36
									
								
								applications/nfc/scenes/nfc_scene_read_emv_data_success.c → applications/nfc/scenes/nfc_scene_emv_read_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -2,7 +2,7 @@ | |||||||
| #include "../helpers/nfc_emv_parser.h" | #include "../helpers/nfc_emv_parser.h" | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_emv_data_success_widget_callback( | void nfc_scene_emv_read_success_widget_callback( | ||||||
|     GuiButtonType result, |     GuiButtonType result, | ||||||
|     InputType type, |     InputType type, | ||||||
|     void* context) { |     void* context) { | ||||||
| @ -12,7 +12,7 @@ void nfc_scene_read_emv_data_success_widget_callback( | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_emv_data_success_on_enter(void* context) { | void nfc_scene_emv_read_success_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     EmvData* emv_data = &nfc->dev->dev_data.emv_data; |     EmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; |     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||||
| @ -23,17 +23,9 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | |||||||
|     widget_add_frame_element(nfc->widget, 0, 0, 128, 64, 6); |     widget_add_frame_element(nfc->widget, 0, 0, 128, 64, 6); | ||||||
|     // Add buttons
 |     // Add buttons
 | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
|         nfc->widget, |         nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_emv_read_success_widget_callback, nfc); | ||||||
|         GuiButtonTypeLeft, |  | ||||||
|         "Back", |  | ||||||
|         nfc_scene_read_emv_data_success_widget_callback, |  | ||||||
|         nfc); |  | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
|         nfc->widget, |         nfc->widget, GuiButtonTypeRight, "Save", nfc_scene_emv_read_success_widget_callback, nfc); | ||||||
|         GuiButtonTypeRight, |  | ||||||
|         "Save", |  | ||||||
|         nfc_scene_read_emv_data_success_widget_callback, |  | ||||||
|         nfc); |  | ||||||
|     // Add card name
 |     // Add card name
 | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev->dev_data.emv_data.name); |         nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev->dev_data.emv_data.name); | ||||||
| @ -103,25 +95,17 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | |||||||
|         widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str); |         widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Send notification
 |  | ||||||
|     if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvDataSuccess) == |  | ||||||
|        NFC_SEND_NOTIFICATION_TRUE) { |  | ||||||
|         notification_message(nfc->notifications, &sequence_success); |  | ||||||
|         scene_manager_set_scene_state( |  | ||||||
|             nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_FALSE); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_emv_read_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == GuiButtonTypeLeft) { |         if(event.event == GuiButtonTypeLeft) { | ||||||
|             consumed = scene_manager_search_and_switch_to_previous_scene( |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); | ||||||
|                 nfc->scene_manager, NfcSceneReadEmvAppSuccess); |             consumed = true; | ||||||
|         } else if(event.event == GuiButtonTypeRight) { |         } else if(event.event == GuiButtonTypeRight) { | ||||||
|             // Clear device name
 |             // Clear device name
 | ||||||
|             nfc_device_set_name(nfc->dev, ""); |             nfc_device_set_name(nfc->dev, ""); | ||||||
| @ -130,13 +114,13 @@ bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent e | |||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|         consumed = scene_manager_search_and_switch_to_previous_scene( |         scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); | ||||||
|             nfc->scene_manager, NfcSceneReadEmvAppSuccess); |         consumed = true; | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_emv_data_success_on_exit(void* context) { | void nfc_scene_emv_read_success_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
							
								
								
									
										47
									
								
								applications/nfc/scenes/nfc_scene_exit_confirm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								applications/nfc/scenes/nfc_scene_exit_confirm.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | void nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_exit_confirm_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Exit"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "Stay"); | ||||||
|  |     dialog_ex_set_header(dialog_ex, "Exit to NFC menu?", 64, 11, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_text( | ||||||
|  |         dialog_ex, "All unsaved data\nwill be lost.", 64, 25, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_context(dialog_ex, nfc); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == DialogExResultRight) { | ||||||
|  |             consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||||
|  |         } else if(event.event == DialogExResultLeft) { | ||||||
|  |             consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 nfc->scene_manager, NfcSceneStart); | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_exit_confirm_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Clean view
 | ||||||
|  |     dialog_ex_reset(nfc->dialog_ex); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								applications/nfc/scenes/nfc_scene_extra_actions.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								applications/nfc/scenes/nfc_scene_extra_actions.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexMfClassicKeys, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_extra_actions_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     Submenu* submenu = nfc->submenu; | ||||||
|  | 
 | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Mf Classic Keys", | ||||||
|  |         SubmenuIndexMfClassicKeys, | ||||||
|  |         nfc_scene_extra_actions_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SubmenuIndexMfClassicKeys) { | ||||||
|  |             if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_extra_actions_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     submenu_reset(nfc->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DictAttackStateIdle, | ||||||
|  |     DictAttackStateUserDictInProgress, | ||||||
|  |     DictAttackStateFlipperDictInProgress, | ||||||
|  | } DictAttackState; | ||||||
|  | 
 | ||||||
|  | bool nfc_dict_attack_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, event); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_dict_attack_dict_attack_result_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackSkip); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { | ||||||
|  |     MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; | ||||||
|  |     uint8_t sectors_read = 0; | ||||||
|  |     uint8_t keys_found = 0; | ||||||
|  | 
 | ||||||
|  |     // Calculate found keys and read sectors
 | ||||||
|  |     mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); | ||||||
|  |     dict_attack_set_keys_found(nfc->dict_attack, keys_found); | ||||||
|  |     dict_attack_set_sector_read(nfc->dict_attack, sectors_read); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { | ||||||
|  |     MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; | ||||||
|  |     NfcWorkerState worker_state = NfcWorkerStateReady; | ||||||
|  | 
 | ||||||
|  |     // Identify scene state
 | ||||||
|  |     if(state == DictAttackStateIdle) { | ||||||
|  |         if(mf_classic_dict_check_presence(MfClassicDictTypeUser)) { | ||||||
|  |             state = DictAttackStateUserDictInProgress; | ||||||
|  |         } else { | ||||||
|  |             state = DictAttackStateFlipperDictInProgress; | ||||||
|  |         } | ||||||
|  |     } else if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |         state = DictAttackStateFlipperDictInProgress; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |         worker_state = NfcWorkerStateMfClassicUserDictAttack; | ||||||
|  |         dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); | ||||||
|  |     } else if(state == DictAttackStateFlipperDictInProgress) { | ||||||
|  |         worker_state = NfcWorkerStateMfClassicFlipperDictAttack; | ||||||
|  |         dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); | ||||||
|  |     } | ||||||
|  |     scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); | ||||||
|  |     dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); | ||||||
|  |     dict_attack_set_current_sector(nfc->dict_attack, 0); | ||||||
|  |     dict_attack_set_card_detected(nfc->dict_attack, data->type); | ||||||
|  |     nfc_scene_mf_classic_dict_attack_update_view(nfc); | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     nfc_scene_mf_classic_dict_attack_prepare_view(nfc, DictAttackStateIdle); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||||
|  |     nfc_blink_start(nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     uint32_t state = | ||||||
|  |         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack); | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcWorkerEventSuccess) { | ||||||
|  |             if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |                 nfc_worker_stop(nfc->worker); | ||||||
|  |                 nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); | ||||||
|  |                 consumed = true; | ||||||
|  |             } else { | ||||||
|  |                 notification_message(nfc->notifications, &sequence_success); | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } else if(event.event == NfcWorkerEventAborted) { | ||||||
|  |             if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |                 nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); | ||||||
|  |                 consumed = true; | ||||||
|  |             } else { | ||||||
|  |                 notification_message(nfc->notifications, &sequence_success); | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } else if(event.event == NfcWorkerEventCardDetected) { | ||||||
|  |             dict_attack_set_card_detected(nfc->dict_attack, data->type); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventNoCardDetected) { | ||||||
|  |             dict_attack_set_card_removed(nfc->dict_attack); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventFoundKeyA) { | ||||||
|  |             dict_attack_inc_keys_found(nfc->dict_attack); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventFoundKeyB) { | ||||||
|  |             dict_attack_inc_keys_found(nfc->dict_attack); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventNewSector) { | ||||||
|  |             nfc_scene_mf_classic_dict_attack_update_view(nfc); | ||||||
|  |             dict_attack_inc_current_sector(nfc->dict_attack); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcCustomEventDictAttackSkip) { | ||||||
|  |             if(state == DictAttackStateUserDictInProgress) { | ||||||
|  |                 nfc_worker_stop(nfc->worker); | ||||||
|  |                 consumed = true; | ||||||
|  |             } else if(state == DictAttackStateFlipperDictInProgress) { | ||||||
|  |                 nfc_worker_stop(nfc->worker); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  |     dict_attack_reset(nfc->dict_attack); | ||||||
|  |     nfc_blink_stop(nfc); | ||||||
|  | } | ||||||
| @ -4,51 +4,52 @@ | |||||||
| #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) | #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) | ||||||
| #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) | #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) | ||||||
| 
 | 
 | ||||||
| void nfc_emulate_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) { | bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|     UNUSED(event); |     UNUSED(event); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_CHANGED); |         nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_CHANGED); | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_emulate_mifare_classic_on_enter(void* context) { | void nfc_scene_mf_classic_emulate_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); |     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = nfc->popup; |     Popup* popup = nfc->popup; | ||||||
|     if(strcmp(nfc->dev->dev_name, "")) { |     if(strcmp(nfc->dev->dev_name, "")) { | ||||||
|         nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); |         nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); | ||||||
|  |     } else { | ||||||
|  |         nfc_text_store_set(nfc, "Emulating\nMf Classic", nfc->dev->dev_name); | ||||||
|     } |     } | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); |     popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); | ||||||
|     popup_set_header(popup, "Emulating\nMf Classic", 56, 31, AlignLeft, AlignTop); |     popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); | ||||||
| 
 | 
 | ||||||
|     // Setup and start worker
 |     // Setup and start worker
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|     nfc_worker_start( |     nfc_worker_start( | ||||||
|         nfc->worker, |         nfc->worker, | ||||||
|         NfcWorkerStateEmulateMifareClassic, |         NfcWorkerStateMfClassicEmulate, | ||||||
|         &nfc->dev->dev_data, |         &nfc->dev->dev_data, | ||||||
|         nfc_emulate_mifare_classic_worker_callback, |         nfc_mf_classic_emulate_worker_callback, | ||||||
|         nfc); |         nfc); | ||||||
|     nfc_blink_start(nfc); |     nfc_blink_start(nfc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeTick) { |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|         consumed = true; |  | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |  | ||||||
|         // Stop worker
 |         // Stop worker
 | ||||||
|         nfc_worker_stop(nfc->worker); |         nfc_worker_stop(nfc->worker); | ||||||
|         // Check if data changed and save in shadow file
 |         // Check if data changed and save in shadow file
 | ||||||
|         if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareClassic) == |         if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicEmulate) == | ||||||
|            NFC_MF_CLASSIC_DATA_CHANGED) { |            NFC_MF_CLASSIC_DATA_CHANGED) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_NOT_CHANGED); |                 nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); | ||||||
|             nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); |             nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||||
|         } |         } | ||||||
|         consumed = false; |         consumed = false; | ||||||
| @ -56,7 +57,7 @@ bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_emulate_mifare_classic_on_exit(void* context) { | void nfc_scene_mf_classic_emulate_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
							
								
								
									
										59
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_keys.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_keys.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_keys_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Load flipper dict keys total
 | ||||||
|  |     uint32_t flipper_dict_keys_total = 0; | ||||||
|  |     MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); | ||||||
|  |     if(dict) { | ||||||
|  |         flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); | ||||||
|  |         mf_classic_dict_free(dict); | ||||||
|  |     } | ||||||
|  |     // Load user dict keys total
 | ||||||
|  |     uint32_t user_dict_keys_total = 0; | ||||||
|  |     dict = mf_classic_dict_alloc(MfClassicDictTypeUser); | ||||||
|  |     if(dict) { | ||||||
|  |         user_dict_keys_total = mf_classic_dict_get_total_keys(dict); | ||||||
|  |         mf_classic_dict_free(dict); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     widget_add_string_element( | ||||||
|  |         nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys"); | ||||||
|  |     char temp_str[32]; | ||||||
|  |     snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total); | ||||||
|  |     widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); | ||||||
|  |     snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total); | ||||||
|  |     widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); | ||||||
|  |     widget_add_button_element( | ||||||
|  |         nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == GuiButtonTypeCenter) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_keys_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_keys_add_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     ByteInput* byte_input = nfc->byte_input; | ||||||
|  |     byte_input_set_header_text(byte_input, "Enter the key in hex"); | ||||||
|  |     byte_input_set_result_callback( | ||||||
|  |         byte_input, | ||||||
|  |         nfc_scene_mf_classic_keys_add_byte_input_callback, | ||||||
|  |         NULL, | ||||||
|  |         nfc, | ||||||
|  |         nfc->byte_input_store, | ||||||
|  |         6); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcCustomEventByteInputDone) { | ||||||
|  |             // Add key to dict
 | ||||||
|  |             bool key_added = false; | ||||||
|  |             MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); | ||||||
|  |             if(dict) { | ||||||
|  |                 if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { | ||||||
|  |                     key_added = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(key_added) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||||
|  |             } | ||||||
|  |             mf_classic_dict_free(dict); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_keys_add_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); | ||||||
|  |     byte_input_set_header_text(nfc->byte_input, ""); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								applications/nfc/scenes/nfc_scene_mifare_ul_menu.c → applications/nfc/scenes/nfc_scene_mf_classic_menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										22
									
								
								applications/nfc/scenes/nfc_scene_mifare_ul_menu.c → applications/nfc/scenes/nfc_scene_mf_classic_menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -5,43 +5,43 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexEmulate, |     SubmenuIndexEmulate, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) { | void nfc_scene_mf_classic_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_ul_menu_on_enter(void* context) { | void nfc_scene_mf_classic_menu_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_ul_menu_submenu_callback, nfc); |         submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mifare_ul_menu_submenu_callback, nfc); |         submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc); | ||||||
|     submenu_set_selected_item( |     submenu_set_selected_item( | ||||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu)); |         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu)); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexSave) { |         if(event.event == SubmenuIndexSave) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave); |                 nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); | ||||||
|             nfc->dev->format = NfcDeviceSaveFormatMifareUl; |             nfc->dev->format = NfcDeviceSaveFormatMifareClassic; | ||||||
|             // Clear device name
 |             // Clear device name
 | ||||||
|             nfc_device_set_name(nfc->dev, ""); |             nfc_device_set_name(nfc->dev, ""); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); |                 nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
| @ -52,7 +52,7 @@ bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_ul_menu_on_exit(void* context) { | void nfc_scene_mf_classic_menu_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
							
								
								
									
										104
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_read_success.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_read_success.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_read_success_widget_callback( | ||||||
|  |     GuiButtonType result, | ||||||
|  |     InputType type, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_read_success_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcDeviceData* dev_data = &nfc->dev->dev_data; | ||||||
|  |     MfClassicData* mf_data = &dev_data->mf_classic_data; | ||||||
|  |     string_t str_tmp; | ||||||
|  |     string_init(str_tmp); | ||||||
|  | 
 | ||||||
|  |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     Widget* widget = nfc->widget; | ||||||
|  |     widget_add_button_element( | ||||||
|  |         widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_read_success_widget_callback, nfc); | ||||||
|  |     widget_add_button_element( | ||||||
|  |         widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); | ||||||
|  | 
 | ||||||
|  |     if(string_size(nfc->dev->dev_data.parsed_data)) { | ||||||
|  |         widget_add_text_box_element( | ||||||
|  |             nfc->widget, | ||||||
|  |             0, | ||||||
|  |             0, | ||||||
|  |             128, | ||||||
|  |             32, | ||||||
|  |             AlignLeft, | ||||||
|  |             AlignTop, | ||||||
|  |             string_get_cstr(nfc->dev->dev_data.parsed_data), | ||||||
|  |             true); | ||||||
|  |     } else { | ||||||
|  |         widget_add_string_element( | ||||||
|  |             widget, | ||||||
|  |             0, | ||||||
|  |             0, | ||||||
|  |             AlignLeft, | ||||||
|  |             AlignTop, | ||||||
|  |             FontSecondary, | ||||||
|  |             mf_classic_get_type_str(mf_data->type)); | ||||||
|  |         widget_add_string_element( | ||||||
|  |             widget, 0, 11, AlignLeft, AlignTop, FontSecondary, "ISO 14443-3 (Type A)"); | ||||||
|  |         string_printf(str_tmp, "UID:"); | ||||||
|  |         for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) { | ||||||
|  |             string_cat_printf(str_tmp, " %02X", dev_data->nfc_data.uid[i]); | ||||||
|  |         } | ||||||
|  |         widget_add_string_element( | ||||||
|  |             widget, 0, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); | ||||||
|  |         uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type); | ||||||
|  |         uint8_t keys_total = sectors_total * 2; | ||||||
|  |         uint8_t keys_found = 0; | ||||||
|  |         uint8_t sectors_read = 0; | ||||||
|  |         mf_classic_get_read_sectors_and_keys(mf_data, §ors_read, &keys_found); | ||||||
|  |         string_printf(str_tmp, "Keys Found: %d/%d", keys_found, keys_total); | ||||||
|  |         widget_add_string_element( | ||||||
|  |             widget, 0, 33, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); | ||||||
|  |         string_printf(str_tmp, "Sectors Read: %d/%d", sectors_read, sectors_total); | ||||||
|  |         widget_add_string_element( | ||||||
|  |             widget, 0, 44, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(str_tmp); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_mf_classic_read_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == GuiButtonTypeLeft) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == GuiButtonTypeRight) { | ||||||
|  |             // Clear device name
 | ||||||
|  |             nfc_device_set_name(nfc->dev, ""); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_mf_classic_read_success_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  | } | ||||||
| @ -1,15 +1,15 @@ | |||||||
| #include "../nfc_i.h" | #include "../nfc_i.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "NfcSceneMifareDesfireApp" | #define TAG "NfcSceneMfDesfireApp" | ||||||
| 
 | 
 | ||||||
| enum SubmenuIndex { | enum SubmenuIndex { | ||||||
|     SubmenuIndexAppInfo, |     SubmenuIndexAppInfo, | ||||||
|     SubmenuIndexDynamic, // dynamic indexes start here
 |     SubmenuIndexDynamic, // dynamic indexes start here
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { | MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) { | ||||||
|     uint32_t app_idx = |     uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> | ||||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp) >> 1; |                        1; | ||||||
|     MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; |     MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; | ||||||
|     for(uint32_t i = 0; i < app_idx && app; i++) { |     for(uint32_t i = 0; i < app_idx && app; i++) { | ||||||
|         app = app->next; |         app = app->next; | ||||||
| @ -17,16 +17,16 @@ MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { | |||||||
|     return app; |     return app; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) { | void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_app_on_enter(void* context) { | void nfc_scene_mf_desfire_app_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
|     MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); |     MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); | ||||||
|     if(!app) { |     if(!app) { | ||||||
|         popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); |         popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); | ||||||
|         popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); |         popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); | ||||||
| @ -45,11 +45,7 @@ void nfc_scene_mifare_desfire_app_on_enter(void* context) { | |||||||
|     text_box_set_font(nfc->text_box, TextBoxFontHex); |     text_box_set_font(nfc->text_box, TextBoxFontHex); | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, |         submenu, "App info", SubmenuIndexAppInfo, nfc_scene_mf_desfire_app_submenu_callback, nfc); | ||||||
|         "App info", |  | ||||||
|         SubmenuIndexAppInfo, |  | ||||||
|         nfc_scene_mifare_desfire_app_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
| 
 | 
 | ||||||
|     uint16_t cap = NFC_TEXT_STORE_SIZE; |     uint16_t cap = NFC_TEXT_STORE_SIZE; | ||||||
|     char* buf = nfc->text_store; |     char* buf = nfc->text_store; | ||||||
| @ -65,20 +61,19 @@ void nfc_scene_mifare_desfire_app_on_enter(void* context) { | |||||||
|         char* label = buf; |         char* label = buf; | ||||||
|         cap -= size + 1; |         cap -= size + 1; | ||||||
|         buf += size + 1; |         buf += size + 1; | ||||||
|         submenu_add_item( |         submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc); | ||||||
|             submenu, label, idx++, nfc_scene_mifare_desfire_app_submenu_callback, nfc); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp); |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); |         MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); | ||||||
|         TextBox* text_box = nfc->text_box; |         TextBox* text_box = nfc->text_box; | ||||||
|         string_reset(nfc->text_box_store); |         string_reset(nfc->text_box_store); | ||||||
|         if(event.event == SubmenuIndexAppInfo) { |         if(event.event == SubmenuIndexAppInfo) { | ||||||
| @ -95,14 +90,13 @@ bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent even | |||||||
|             mf_df_cat_file(file, nfc->text_box_store); |             mf_df_cat_file(file, nfc->text_box_store); | ||||||
|         } |         } | ||||||
|         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); |         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1); |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1); | ||||||
|         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); |         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|         if(state & 1) { |         if(state & 1) { | ||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state & ~1); | ||||||
|                 nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -110,7 +104,7 @@ bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent even | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_app_on_exit(void* context) { | void nfc_scene_mf_desfire_app_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear views
 |     // Clear views
 | ||||||
| @ -1,6 +1,6 @@ | |||||||
| #include "../nfc_i.h" | #include "../nfc_i.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "NfcSceneMifareDesfireData" | #define TAG "NfcSceneMfDesfireData" | ||||||
| 
 | 
 | ||||||
| enum { | enum { | ||||||
|     MifareDesfireDataStateMenu, |     MifareDesfireDataStateMenu, | ||||||
| @ -12,16 +12,16 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexDynamic, // dynamic indexes start here
 |     SubmenuIndexDynamic, // dynamic indexes start here
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_data_submenu_callback(void* context, uint32_t index) { | void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { | ||||||
|     Nfc* nfc = (Nfc*)context; |     Nfc* nfc = (Nfc*)context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_data_on_enter(void* context) { | void nfc_scene_mf_desfire_data_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); | ||||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; |     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||||
| 
 | 
 | ||||||
|     text_box_set_font(nfc->text_box, TextBoxFontHex); |     text_box_set_font(nfc->text_box, TextBoxFontHex); | ||||||
| @ -30,7 +30,7 @@ void nfc_scene_mifare_desfire_data_on_enter(void* context) { | |||||||
|         submenu, |         submenu, | ||||||
|         "Card info", |         "Card info", | ||||||
|         SubmenuIndexCardInfo, |         SubmenuIndexCardInfo, | ||||||
|         nfc_scene_mifare_desfire_data_submenu_callback, |         nfc_scene_mf_desfire_data_submenu_callback, | ||||||
|         nfc); |         nfc); | ||||||
| 
 | 
 | ||||||
|     uint16_t cap = NFC_TEXT_STORE_SIZE; |     uint16_t cap = NFC_TEXT_STORE_SIZE; | ||||||
| @ -46,24 +46,23 @@ void nfc_scene_mifare_desfire_data_on_enter(void* context) { | |||||||
|         char* label = buf; |         char* label = buf; | ||||||
|         cap -= size + 1; |         cap -= size + 1; | ||||||
|         buf += size + 1; |         buf += size + 1; | ||||||
|         submenu_add_item( |         submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_data_submenu_callback, nfc); | ||||||
|             submenu, label, idx++, nfc_scene_mifare_desfire_data_submenu_callback, nfc); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(state >= MifareDesfireDataStateItem) { |     if(state >= MifareDesfireDataStateItem) { | ||||||
|         submenu_set_selected_item( |         submenu_set_selected_item( | ||||||
|             nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); |             nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); | ||||||
|         scene_manager_set_scene_state( |         scene_manager_set_scene_state( | ||||||
|             nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); |             nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); | ||||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; |     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
| @ -75,23 +74,22 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve | |||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, |                 nfc->scene_manager, | ||||||
|                 NfcSceneMifareDesfireData, |                 NfcSceneMfDesfireData, | ||||||
|                 MifareDesfireDataStateItem + SubmenuIndexCardInfo); |                 MifareDesfireDataStateItem + SubmenuIndexCardInfo); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else { |         } else { | ||||||
|             uint16_t index = event.event - SubmenuIndexDynamic; |             uint16_t index = event.event - SubmenuIndexDynamic; | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateItem + index); |                 nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateItem + index); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); | ||||||
|                 nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|         if(state >= MifareDesfireDataStateItem) { |         if(state >= MifareDesfireDataStateItem) { | ||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); |                 nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -99,7 +97,7 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_data_on_exit(void* context) { | void nfc_scene_mf_desfire_data_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear views
 |     // Clear views
 | ||||||
| @ -4,33 +4,32 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexSave, |     SubmenuIndexSave, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) { | void nfc_scene_mf_desfire_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_menu_on_enter(void* context) { | void nfc_scene_mf_desfire_menu_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_desfire_menu_submenu_callback, nfc); |         submenu, "Save", SubmenuIndexSave, nfc_scene_mf_desfire_menu_submenu_callback, nfc); | ||||||
|     submenu_set_selected_item( |     submenu_set_selected_item( | ||||||
|         nfc->submenu, |         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMenu)); | ||||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireMenu)); |  | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexSave) { |         if(event.event == SubmenuIndexSave) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareDesfireMenu, SubmenuIndexSave); |                 nfc->scene_manager, NfcSceneMfDesfireMenu, SubmenuIndexSave); | ||||||
|             nfc->dev->format = NfcDeviceSaveFormatMifareDesfire; |             nfc->dev->format = NfcDeviceSaveFormatMifareDesfire; | ||||||
|             // Clear device name
 |             // Clear device name
 | ||||||
|             nfc_device_set_name(nfc->dev, ""); |             nfc_device_set_name(nfc->dev, ""); | ||||||
| @ -42,7 +41,7 @@ bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent eve | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_desfire_menu_on_exit(void* context) { | void nfc_scene_mf_desfire_menu_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
| @ -4,22 +4,22 @@ | |||||||
| #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | ||||||
| 
 | 
 | ||||||
| enum { | enum { | ||||||
|     ReadMifareDesfireSuccessStateShowUID, |     MfDesfireReadSuccessStateShowUID, | ||||||
|     ReadMifareDesfireSuccessStateShowData, |     MfDesfireReadSuccessStateShowData, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) { | void nfc_scene_mf_desfire_read_success_dialog_callback(DialogExResult result, void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { | void nfc_scene_mf_desfire_read_success_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; |     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||||
|     DialogEx* dialog_ex = nfc->dialog_ex; |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Back"); |     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||||
|     dialog_ex_set_center_button_text(dialog_ex, "Data"); |     dialog_ex_set_center_button_text(dialog_ex, "Data"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|     dialog_ex_set_icon(dialog_ex, 8, 16, &I_Medium_chip_22x21); |     dialog_ex_set_icon(dialog_ex, 8, 16, &I_Medium_chip_22x21); | ||||||
| @ -55,41 +55,40 @@ void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { | |||||||
|         n_files == 1 ? "" : "s"); |         n_files == 1 ? "" : "s"); | ||||||
|     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 6, AlignLeft, AlignTop); |     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 6, AlignLeft, AlignTop); | ||||||
|     dialog_ex_set_context(dialog_ex, nfc); |     dialog_ex_set_context(dialog_ex, nfc); | ||||||
|     dialog_ex_set_result_callback( |     dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_desfire_read_success_dialog_callback); | ||||||
|         dialog_ex, nfc_scene_read_mifare_desfire_success_dialog_callback); |  | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, |         nfc->scene_manager, NfcSceneMfDesfireReadSuccess, MfDesfireReadSuccessStateShowUID); | ||||||
|         NfcSceneReadMifareDesfireSuccess, |  | ||||||
|         ReadMifareDesfireSuccessStateShowUID); |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_desfire_read_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     uint32_t state = |     uint32_t state = | ||||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); |         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) { |         if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultLeft) { | ||||||
|             scene_manager_previous_scene(nfc->scene_manager); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if( |         } else if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultCenter) { | ||||||
|             state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultCenter) { |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultRight) { |         } else if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultRight) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireMenu); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|         if(state == ReadMifareDesfireSuccessStateShowData) { |         if(state == MfDesfireReadSuccessStateShowData) { | ||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, |                 nfc->scene_manager, | ||||||
|                 NfcSceneReadMifareDesfireSuccess, |                 NfcSceneMfDesfireReadSuccess, | ||||||
|                 ReadMifareDesfireSuccessStateShowUID); |                 MfDesfireReadSuccessStateShowUID); | ||||||
|  |             consumed = true; | ||||||
|  |         } else { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -97,7 +96,7 @@ bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerE | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_desfire_success_on_exit(void* context) { | void nfc_scene_mf_desfire_read_success_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clean dialog
 |     // Clean dialog
 | ||||||
| @ -4,51 +4,52 @@ | |||||||
| #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) | #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) | ||||||
| #define NFC_MF_UL_DATA_CHANGED (1UL) | #define NFC_MF_UL_DATA_CHANGED (1UL) | ||||||
| 
 | 
 | ||||||
| void nfc_emulate_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { | bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|     UNUSED(event); |     UNUSED(event); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); |         nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_CHANGED); | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_emulate_mifare_ul_on_enter(void* context) { | void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); |     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = nfc->popup; |     Popup* popup = nfc->popup; | ||||||
|     if(strcmp(nfc->dev->dev_name, "")) { |     if(strcmp(nfc->dev->dev_name, "")) { | ||||||
|         nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); |         nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); | ||||||
|  |     } else { | ||||||
|  |         nfc_text_store_set(nfc, "Emulating\nMf Ultralight", nfc->dev->dev_name); | ||||||
|     } |     } | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); |     popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); | ||||||
|     popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop); |     popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); | ||||||
| 
 | 
 | ||||||
|     // Setup and start worker
 |     // Setup and start worker
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|     nfc_worker_start( |     nfc_worker_start( | ||||||
|         nfc->worker, |         nfc->worker, | ||||||
|         NfcWorkerStateEmulateMifareUltralight, |         NfcWorkerStateMfUltralightEmulate, | ||||||
|         &nfc->dev->dev_data, |         &nfc->dev->dev_data, | ||||||
|         nfc_emulate_mifare_ul_worker_callback, |         nfc_mf_ultralight_emulate_worker_callback, | ||||||
|         nfc); |         nfc); | ||||||
|     nfc_blink_start(nfc); |     nfc_blink_start(nfc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeTick) { |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|         consumed = true; |  | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |  | ||||||
|         // Stop worker
 |         // Stop worker
 | ||||||
|         nfc_worker_stop(nfc->worker); |         nfc_worker_stop(nfc->worker); | ||||||
|         // Check if data changed and save in shadow file
 |         // Check if data changed and save in shadow file
 | ||||||
|         if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareUl) == |         if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate) == | ||||||
|            NFC_MF_UL_DATA_CHANGED) { |            NFC_MF_UL_DATA_CHANGED) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED); |                 nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED); | ||||||
|             nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); |             nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||||
|         } |         } | ||||||
|         consumed = false; |         consumed = false; | ||||||
| @ -56,7 +57,7 @@ bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_emulate_mifare_ul_on_exit(void* context) { | void nfc_scene_mf_ultralight_emulate_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
| @ -5,47 +5,47 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexEmulate, |     SubmenuIndexEmulate, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_classic_menu_submenu_callback(void* context, uint32_t index) { | void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_classic_menu_on_enter(void* context) { | void nfc_scene_mf_ultralight_menu_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_classic_menu_submenu_callback, nfc); |         submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, |         submenu, | ||||||
|         "Emulate", |         "Emulate", | ||||||
|         SubmenuIndexEmulate, |         SubmenuIndexEmulate, | ||||||
|         nfc_scene_mifare_classic_menu_submenu_callback, |         nfc_scene_mf_ultralight_menu_submenu_callback, | ||||||
|         nfc); |         nfc); | ||||||
|     submenu_set_selected_item( |     submenu_set_selected_item( | ||||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu)); |         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu)); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexSave) { |         if(event.event == SubmenuIndexSave) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave); |                 nfc->scene_manager, NfcSceneMfUltralightMenu, SubmenuIndexSave); | ||||||
|             nfc->dev->format = NfcDeviceSaveFormatMifareClassic; |             nfc->dev->format = NfcDeviceSaveFormatMifareUl; | ||||||
|             // Clear device name
 |             // Clear device name
 | ||||||
|             nfc_device_set_name(nfc->dev, ""); |             nfc_device_set_name(nfc->dev, ""); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); |                 nfc->scene_manager, NfcSceneMfUltralightMenu, SubmenuIndexEmulate); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
| @ -56,7 +56,7 @@ bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent eve | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_mifare_classic_menu_on_exit(void* context) { | void nfc_scene_mf_ultralight_menu_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
| @ -8,19 +8,16 @@ enum { | |||||||
|     ReadMifareUlStateShowData, |     ReadMifareUlStateShowData, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, void* context) { | void nfc_scene_mf_ultralight_read_success_dialog_callback(DialogExResult result, void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_ul_success_on_enter(void* context) { | void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Send notification
 |  | ||||||
|     notification_message(nfc->notifications, &sequence_success); |  | ||||||
| 
 |  | ||||||
|     // Setup dialog view
 |     // Setup dialog view
 | ||||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; |     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||||
|     MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; |     MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; | ||||||
| @ -48,7 +45,7 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) { | |||||||
|         data->uid[6]); |         data->uid[6]); | ||||||
|     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop); |     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop); | ||||||
|     dialog_ex_set_context(dialog_ex, nfc); |     dialog_ex_set_context(dialog_ex, nfc); | ||||||
|     dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_mifare_ul_success_dialog_callback); |     dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_read_success_dialog_callback); | ||||||
| 
 | 
 | ||||||
|     // Setup TextBox view
 |     // Setup TextBox view
 | ||||||
|     TextBox* text_box = nfc->text_box; |     TextBox* text_box = nfc->text_box; | ||||||
| @ -63,34 +60,37 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) { | |||||||
|     text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); |     text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID); |         nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowUID); | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     uint32_t state = |     uint32_t state = | ||||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess); |         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { |         if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { | ||||||
|             scene_manager_previous_scene(nfc->scene_manager); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultRight) { |         } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultRight) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareUlMenu); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultCenter) { |         } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultCenter) { | ||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowData); |                 nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowData); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeBack) { |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|         if(state == ReadMifareUlStateShowData) { |         if(state == ReadMifareUlStateShowData) { | ||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID); |                 nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowUID); | ||||||
|  |             consumed = true; | ||||||
|  |         } else { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -98,7 +98,7 @@ bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_read_mifare_ul_success_on_exit(void* context) { | void nfc_scene_mf_ultralight_read_success_on_exit(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     // Clean views
 |     // Clean views
 | ||||||
							
								
								
									
										108
									
								
								applications/nfc/scenes/nfc_scene_read.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								applications/nfc/scenes/nfc_scene_read.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     NfcSceneReadStateIdle, | ||||||
|  |     NfcSceneReadStateDetecting, | ||||||
|  |     NfcSceneReadStateReading, | ||||||
|  | } NfcSceneReadState; | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_read_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     if(event == NfcWorkerEventReadMfClassicLoadKeyCache) { | ||||||
|  |         consumed = nfc_device_load_key_cache(nfc->dev); | ||||||
|  |     } else { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, event); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { | ||||||
|  |     uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneRead); | ||||||
|  |     if(curr_state != state) { | ||||||
|  |         if(state == NfcSceneReadStateDetecting) { | ||||||
|  |             popup_set_header(nfc->popup, "Detecting\nNFC card", 90, 24, AlignCenter, AlignTop); | ||||||
|  |             popup_set_icon(nfc->popup, 5, 7, &I_NFC_manual); | ||||||
|  |         } else if(state == NfcSceneReadStateReading) { | ||||||
|  |             popup_set_header( | ||||||
|  |                 nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); | ||||||
|  |             popup_set_icon(nfc->popup, 19, 23, &A_Loading_24); | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||||
|  | 
 | ||||||
|  |     nfc_device_clear(nfc->dev); | ||||||
|  |     // Setup view
 | ||||||
|  |     nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|  |     // Start worker
 | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, NfcWorkerStateRead, &nfc->dev->dev_data, nfc_scene_read_worker_callback, nfc); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_start(nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if((event.event == NfcWorkerEventReadUidNfcB) || | ||||||
|  |            (event.event == NfcWorkerEventReadUidNfcF) || | ||||||
|  |            (event.event == NfcWorkerEventReadUidNfcV) || | ||||||
|  |            (event.event == NfcWorkerEventReadUidNfcA)) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventReadMfUltralight) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventReadMfClassicDone) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventReadMfDesfire) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventReadBankCard) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { | ||||||
|  |             if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventCardDetected) { | ||||||
|  |             nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventNoCardDetected) { | ||||||
|  |             nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_read_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  |     // Clear view
 | ||||||
|  |     popup_reset(nfc->popup); | ||||||
|  |     scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, NfcSceneReadStateIdle); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_stop(nfc); | ||||||
|  | } | ||||||
| @ -1,51 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| void nfc_read_card_worker_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     UNUSED(event); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_card_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |  | ||||||
|     Popup* popup = nfc->popup; |  | ||||||
|     popup_set_header(popup, "Detecting\nNFC card", 70, 34, AlignLeft, AlignTop); |  | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); |  | ||||||
| 
 |  | ||||||
|     // Start worker
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |  | ||||||
|     nfc_worker_start( |  | ||||||
|         nfc->worker, NfcWorkerStateDetect, &nfc->dev->dev_data, nfc_read_card_worker_callback, nfc); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_start(nfc); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_card_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Stop worker
 |  | ||||||
|     nfc_worker_stop(nfc->worker); |  | ||||||
|     // Clear view
 |  | ||||||
|     popup_reset(nfc->popup); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_stop(nfc); |  | ||||||
| } |  | ||||||
| @ -22,9 +22,6 @@ void nfc_scene_read_card_success_on_enter(void* context) { | |||||||
|     string_init(uid_str); |     string_init(uid_str); | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Send notification
 |  | ||||||
|     notification_message(nfc->notifications, &sequence_success); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; |     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||||
|     Widget* widget = nfc->widget; |     Widget* widget = nfc->widget; | ||||||
| @ -38,18 +35,12 @@ void nfc_scene_read_card_success_on_enter(void* context) { | |||||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc); |         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc); | ||||||
|     if(data->type == FuriHalNfcTypeA) { |     if(data->type == FuriHalNfcTypeA) { | ||||||
|         widget_add_button_element( |         widget_add_button_element( | ||||||
|             widget, GuiButtonTypeRight, "More", nfc_scene_read_card_success_widget_callback, nfc); |             widget, GuiButtonTypeRight, "Save", nfc_scene_read_card_success_widget_callback, nfc); | ||||||
|         widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21); |         widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21); | ||||||
|         string_cat_printf(data_str, " may be:"); |  | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             widget, 37, 12, AlignLeft, AlignBottom, FontPrimary, string_get_cstr(data_str)); |             widget, 37, 12, AlignLeft, AlignBottom, FontPrimary, string_get_cstr(data_str)); | ||||||
|         string_printf( |         string_printf( | ||||||
|             data_str, |             data_str, "ATQA: %02X%02X\nSAK: %02X", data->atqa[0], data->atqa[1], data->sak); | ||||||
|             "%s\nATQA: %02X%02X SAK: %02X", |  | ||||||
|             nfc_guess_protocol(nfc->dev->dev_data.protocol), |  | ||||||
|             data->atqa[0], |  | ||||||
|             data->atqa[1], |  | ||||||
|             data->sak); |  | ||||||
|         widget_add_string_multiline_element( |         widget_add_string_multiline_element( | ||||||
|             widget, 37, 16, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); |             widget, 37, 16, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
| @ -69,16 +60,15 @@ void nfc_scene_read_card_success_on_enter(void* context) { | |||||||
| 
 | 
 | ||||||
| bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; |  | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == GuiButtonTypeLeft) { |         if(event.event == GuiButtonTypeLeft) { | ||||||
|             consumed = scene_manager_previous_scene(nfc->scene_manager); |             consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||||
|         } else if(data->type == FuriHalNfcTypeA && event.event == GuiButtonTypeRight) { |         } else if(event.event == GuiButtonTypeRight) { | ||||||
|             // Clear device name
 |             nfc->dev->format = NfcDeviceSaveFormatUid; | ||||||
|             nfc_device_set_name(nfc->dev, ""); |             nfc_device_set_name(nfc->dev, ""); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,58 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| void nfc_read_emv_app_worker_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     UNUSED(event); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_app_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |  | ||||||
|     Popup* popup = nfc->popup; |  | ||||||
|     popup_set_header(popup, "Reading\nbank card", 70, 34, AlignLeft, AlignTop); |  | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |  | ||||||
|     // Start worker
 |  | ||||||
|     nfc_worker_start( |  | ||||||
|         nfc->worker, |  | ||||||
|         NfcWorkerStateReadEMVApp, |  | ||||||
|         &nfc->dev->dev_data, |  | ||||||
|         nfc_read_emv_app_worker_callback, |  | ||||||
|         nfc); |  | ||||||
|     nfc_blink_start(nfc); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_TRUE); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_app_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Stop worker
 |  | ||||||
|     nfc_worker_stop(nfc->worker); |  | ||||||
| 
 |  | ||||||
|     // Clear view
 |  | ||||||
|     popup_reset(nfc->popup); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_stop(nfc); |  | ||||||
| } |  | ||||||
| @ -1,83 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| #include "../helpers/nfc_emv_parser.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_app_widget_callback(GuiButtonType result, InputType type, void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     if(type == InputTypeShort) { |  | ||||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_app_success_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |  | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; |  | ||||||
|     EmvData* emv_data = &nfc->dev->dev_data.emv_data; |  | ||||||
|     Widget* widget = nfc->widget; |  | ||||||
|     widget_add_button_element( |  | ||||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_emv_app_widget_callback, nfc); |  | ||||||
|     widget_add_button_element( |  | ||||||
|         widget, GuiButtonTypeRight, "Run app", nfc_scene_read_emv_app_widget_callback, nfc); |  | ||||||
|     widget_add_string_element(widget, 36, 5, AlignLeft, AlignTop, FontPrimary, "Found EMV App"); |  | ||||||
|     widget_add_icon_element(widget, 8, 5, &I_Medium_chip_22x21); |  | ||||||
|     // Display UID
 |  | ||||||
|     string_t temp_str; |  | ||||||
|     string_init_printf(temp_str, "UID:"); |  | ||||||
|     for(size_t i = 0; i < nfc_data->uid_len; i++) { |  | ||||||
|         string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); |  | ||||||
|     } |  | ||||||
|     widget_add_string_element( |  | ||||||
|         widget, 36, 18, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); |  | ||||||
|     string_reset(temp_str); |  | ||||||
|     // Display application
 |  | ||||||
|     string_printf(temp_str, "App: "); |  | ||||||
|     string_t aid; |  | ||||||
|     string_init(aid); |  | ||||||
|     bool aid_found = |  | ||||||
|         nfc_emv_parser_get_aid_name(nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid); |  | ||||||
|     if(!aid_found) { |  | ||||||
|         for(uint8_t i = 0; i < emv_data->aid_len; i++) { |  | ||||||
|             string_cat_printf(aid, "%02X", emv_data->aid[i]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     string_cat(temp_str, aid); |  | ||||||
|     widget_add_string_element( |  | ||||||
|         widget, 7, 29, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); |  | ||||||
|     string_clear(temp_str); |  | ||||||
|     string_clear(aid); |  | ||||||
| 
 |  | ||||||
|     // Send notification
 |  | ||||||
|     if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvAppSuccess) == |  | ||||||
|        NFC_SEND_NOTIFICATION_TRUE) { |  | ||||||
|         notification_message(nfc->notifications, &sequence_success); |  | ||||||
|         scene_manager_set_scene_state( |  | ||||||
|             nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_FALSE); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == GuiButtonTypeLeft) { |  | ||||||
|             consumed = scene_manager_previous_scene(nfc->scene_manager); |  | ||||||
|         } else if(event.event == GuiButtonTypeRight) { |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRunEmvAppConfirm); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_app_success_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Clear views
 |  | ||||||
|     widget_reset(nfc->widget); |  | ||||||
| } |  | ||||||
| @ -1,59 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| void nfc_read_emv_data_worker_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     UNUSED(event); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_data_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |  | ||||||
|     Popup* popup = nfc->popup; |  | ||||||
|     popup_set_header(popup, "Reading\nbank card", 70, 34, AlignLeft, AlignTop); |  | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |  | ||||||
|     // Clear emv data
 |  | ||||||
|     memset(&nfc->dev->dev_data.emv_data, 0, sizeof(nfc->dev->dev_data.emv_data)); |  | ||||||
|     // Start worker
 |  | ||||||
|     nfc_worker_start( |  | ||||||
|         nfc->worker, |  | ||||||
|         NfcWorkerStateReadEMVData, |  | ||||||
|         &nfc->dev->dev_data, |  | ||||||
|         nfc_read_emv_data_worker_callback, |  | ||||||
|         nfc); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_start(nfc); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_TRUE); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_emv_data_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Stop worker
 |  | ||||||
|     nfc_worker_stop(nfc->worker); |  | ||||||
|     // Clear view
 |  | ||||||
|     popup_reset(nfc->popup); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_stop(nfc); |  | ||||||
| } |  | ||||||
| @ -1,96 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| 
 |  | ||||||
| enum { |  | ||||||
|     NfcSceneReadMifareClassicStateInProgress, |  | ||||||
|     NfcSceneReadMifareClassicStateDone, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void nfc_read_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_read_mifare_classic_dict_attack_result_callback(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackDone); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_mifare_classic_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Setup and start worker
 |  | ||||||
|     memset(&nfc->dev->dev_data.mf_classic_data, 0, sizeof(MfClassicData)); |  | ||||||
|     dict_attack_set_result_callback( |  | ||||||
|         nfc->dict_attack, nfc_read_mifare_classic_dict_attack_result_callback, nfc); |  | ||||||
|     scene_manager_set_scene_state( |  | ||||||
|         nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateInProgress); |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); |  | ||||||
|     nfc_worker_start( |  | ||||||
|         nfc->worker, |  | ||||||
|         NfcWorkerStateReadMifareClassic, |  | ||||||
|         &nfc->dev->dev_data, |  | ||||||
|         nfc_read_mifare_classic_worker_callback, |  | ||||||
|         nfc); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_start(nfc); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|         consumed = true; |  | ||||||
|     } else if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventDictAttackDone) { |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareClassicMenu); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventDetectedClassic1k) { |  | ||||||
|             dict_attack_card_detected(nfc->dict_attack, MfClassicType1k); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventDetectedClassic4k) { |  | ||||||
|             dict_attack_card_detected(nfc->dict_attack, MfClassicType4k); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventNewSector) { |  | ||||||
|             dict_attack_inc_curr_sector(nfc->dict_attack); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventFoundKeyA) { |  | ||||||
|             dict_attack_inc_found_key(nfc->dict_attack, MfClassicKeyA); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventFoundKeyB) { |  | ||||||
|             dict_attack_inc_found_key(nfc->dict_attack, MfClassicKeyB); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventNoCardDetected) { |  | ||||||
|             dict_attack_card_removed(nfc->dict_attack); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventSuccess) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); |  | ||||||
|             nfc_blink_stop(nfc); |  | ||||||
|             notification_message(nfc->notifications, &sequence_success); |  | ||||||
|             dict_attack_set_result(nfc->dict_attack, true); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventFail) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); |  | ||||||
|             nfc_blink_stop(nfc); |  | ||||||
|             dict_attack_set_result(nfc->dict_attack, false); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == NfcWorkerEventNoDictFound) { |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_mifare_classic_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     // Stop worker
 |  | ||||||
|     nfc_worker_stop(nfc->worker); |  | ||||||
|     dict_attack_reset(nfc->dict_attack); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_stop(nfc); |  | ||||||
| } |  | ||||||
| @ -1,56 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| void nfc_read_mifare_desfire_worker_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     UNUSED(event); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_mifare_desfire_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |  | ||||||
|     Popup* popup = nfc->popup; |  | ||||||
|     popup_set_header(popup, "Reading\nDESFire", 70, 34, AlignLeft, AlignTop); |  | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |  | ||||||
|     // Start worker
 |  | ||||||
|     nfc_worker_start( |  | ||||||
|         nfc->worker, |  | ||||||
|         NfcWorkerStateReadMifareDesfire, |  | ||||||
|         &nfc->dev->dev_data, |  | ||||||
|         nfc_read_mifare_desfire_worker_callback, |  | ||||||
|         nfc); |  | ||||||
|     nfc_blink_start(nfc); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |  | ||||||
|             notification_message(nfc->notifications, &sequence_success); |  | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_mifare_desfire_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Stop worker
 |  | ||||||
|     nfc_worker_stop(nfc->worker); |  | ||||||
|     // Clear view
 |  | ||||||
|     popup_reset(nfc->popup); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_stop(nfc); |  | ||||||
| } |  | ||||||
| @ -1,54 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| void nfc_read_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     UNUSED(event); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_mifare_ul_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |  | ||||||
| 
 |  | ||||||
|     // Setup view
 |  | ||||||
|     Popup* popup = nfc->popup; |  | ||||||
|     popup_set_header(popup, "Detecting\nultralight", 70, 34, AlignLeft, AlignTop); |  | ||||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); |  | ||||||
|     // Start worker
 |  | ||||||
|     nfc_worker_start( |  | ||||||
|         nfc->worker, |  | ||||||
|         NfcWorkerStateReadMifareUltralight, |  | ||||||
|         &nfc->dev->dev_data, |  | ||||||
|         nfc_read_mifare_ul_worker_callback, |  | ||||||
|         nfc); |  | ||||||
|     nfc_blink_start(nfc); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_read_mifare_ul_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Stop worker
 |  | ||||||
|     nfc_worker_stop(nfc->worker); |  | ||||||
|     // Clear view
 |  | ||||||
|     popup_reset(nfc->popup); |  | ||||||
| 
 |  | ||||||
|     nfc_blink_stop(nfc); |  | ||||||
| } |  | ||||||
							
								
								
									
										47
									
								
								applications/nfc/scenes/nfc_scene_retry_confirm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								applications/nfc/scenes/nfc_scene_retry_confirm.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | void nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_retry_confirm_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog_ex, "Stay"); | ||||||
|  |     dialog_ex_set_header(dialog_ex, "Retry reading?", 64, 11, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_text( | ||||||
|  |         dialog_ex, "All unsaved data will be\nlost.", 64, 25, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_context(dialog_ex, nfc); | ||||||
|  |     dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == DialogExResultRight) { | ||||||
|  |             consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||||
|  |         } else if(event.event == DialogExResultLeft) { | ||||||
|  |             consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 nfc->scene_manager, NfcSceneRead); | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_retry_confirm_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Clean view
 | ||||||
|  |     dialog_ex_reset(nfc->dialog_ex); | ||||||
|  | } | ||||||
| @ -1,49 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| 
 |  | ||||||
| void nfc_scene_run_emv_app_confirm_dialog_callback(DialogExResult result, void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_run_emv_app_confirm_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     DialogEx* dialog_ex = nfc->dialog_ex; |  | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Back"); |  | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Run"); |  | ||||||
|     dialog_ex_set_header(dialog_ex, "Run EMV app?", 64, 8, AlignCenter, AlignCenter); |  | ||||||
|     dialog_ex_set_text( |  | ||||||
|         dialog_ex, |  | ||||||
|         "It will try to run card's app\nand detect unencrypted\ndata", |  | ||||||
|         64, |  | ||||||
|         18, |  | ||||||
|         AlignCenter, |  | ||||||
|         AlignTop); |  | ||||||
|     dialog_ex_set_context(dialog_ex, nfc); |  | ||||||
|     dialog_ex_set_result_callback(dialog_ex, nfc_scene_run_emv_app_confirm_dialog_callback); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == DialogExResultLeft) { |  | ||||||
|             consumed = scene_manager_previous_scene(nfc->scene_manager); |  | ||||||
|         } else if(event.event == DialogExResultRight) { |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvData); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_run_emv_app_confirm_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     // Clean view
 |  | ||||||
|     dialog_ex_reset(nfc->dialog_ex); |  | ||||||
| } |  | ||||||
| @ -27,13 +27,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == NfcCustomEventViewExit) { |         if(event.event == NfcCustomEventViewExit) { | ||||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) { |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfDesfireMenu)) { | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     nfc->scene_manager, NfcSceneCardMenu); |                     nfc->scene_manager, NfcSceneMfDesfireMenu); | ||||||
|             } else if(scene_manager_has_previous_scene( |  | ||||||
|                           nfc->scene_manager, NfcSceneMifareDesfireMenu)) { |  | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |  | ||||||
|                     nfc->scene_manager, NfcSceneMifareDesfireMenu); |  | ||||||
|             } else { |             } else { | ||||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( |                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     nfc->scene_manager, NfcSceneStart); |                     nfc->scene_manager, NfcSceneStart); | ||||||
|  | |||||||
| @ -61,9 +61,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event); |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event); | ||||||
|         if(event.event == SubmenuIndexEmulate) { |         if(event.event == SubmenuIndexEmulate) { | ||||||
|             if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |             if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -1,82 +0,0 @@ | |||||||
| #include "../nfc_i.h" |  | ||||||
| 
 |  | ||||||
| enum SubmenuIndex { |  | ||||||
|     SubmenuIndexBankCard, |  | ||||||
|     SubmenuIndexMifareUltralight, |  | ||||||
|     SubmenuIdexReadMfClassic, |  | ||||||
|     SubmenuIndexMifareDesfire, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_scripts_menu_on_enter(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     Submenu* submenu = nfc->submenu; |  | ||||||
| 
 |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, |  | ||||||
|         "Read Bank Card", |  | ||||||
|         SubmenuIndexBankCard, |  | ||||||
|         nfc_scene_scripts_menu_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, |  | ||||||
|         "Read Mifare Ultral/Ntag", |  | ||||||
|         SubmenuIndexMifareUltralight, |  | ||||||
|         nfc_scene_scripts_menu_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, |  | ||||||
|         "Read Mifare Classic", |  | ||||||
|         SubmenuIdexReadMfClassic, |  | ||||||
|         nfc_scene_scripts_menu_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, |  | ||||||
|         "Read Mifare DESFire", |  | ||||||
|         SubmenuIndexMifareDesfire, |  | ||||||
|         nfc_scene_scripts_menu_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
|     submenu_set_selected_item( |  | ||||||
|         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); |  | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         if(event.event == SubmenuIndexBankCard) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexBankCard); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == SubmenuIndexMifareUltralight) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == SubmenuIdexReadMfClassic) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIdexReadMfClassic); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); |  | ||||||
|             consumed = true; |  | ||||||
|         } else if(event.event == SubmenuIndexMifareDesfire) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareDesfire); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); |  | ||||||
|             consumed = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void nfc_scene_scripts_menu_on_exit(void* context) { |  | ||||||
|     Nfc* nfc = context; |  | ||||||
|     submenu_reset(nfc->submenu); |  | ||||||
| } |  | ||||||
| @ -2,8 +2,9 @@ | |||||||
| 
 | 
 | ||||||
| enum SubmenuIndex { | enum SubmenuIndex { | ||||||
|     SubmenuIndexRead, |     SubmenuIndexRead, | ||||||
|     SubmenuIndexRunScript, |     SubmenuIndexDetectReader, | ||||||
|     SubmenuIndexSaved, |     SubmenuIndexSaved, | ||||||
|  |     SubmenuIndexExtraAction, | ||||||
|     SubmenuIndexAddManualy, |     SubmenuIndexAddManualy, | ||||||
|     SubmenuIndexDebug, |     SubmenuIndexDebug, | ||||||
| }; | }; | ||||||
| @ -18,15 +19,12 @@ void nfc_scene_start_on_enter(void* context) { | |||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Submenu* submenu = nfc->submenu; |     Submenu* submenu = nfc->submenu; | ||||||
| 
 | 
 | ||||||
|  |     submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Read Card", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); |         submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, |  | ||||||
|         "Run Special Action", |  | ||||||
|         SubmenuIndexRunScript, |  | ||||||
|         nfc_scene_start_submenu_callback, |  | ||||||
|         nfc); |  | ||||||
|     submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); |     submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Add Manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc); |         submenu, "Add Manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc); | ||||||
| 
 | 
 | ||||||
| @ -48,14 +46,17 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexRead) { |         if(event.event == SubmenuIndexRead) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexRunScript) { |         } else if(event.event == SubmenuIndexDetectReader) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexSaved) { |         } else if(event.event == SubmenuIndexSaved) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexExtraAction) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); | ||||||
|  |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexAddManualy) { |         } else if(event.event == SubmenuIndexAddManualy) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  | |||||||
| @ -1,83 +1,60 @@ | |||||||
| #include "dict_attack.h" | #include "dict_attack.h" | ||||||
| #include <m-string.h> |  | ||||||
| 
 | 
 | ||||||
|  | #include <m-string.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DictAttackStateSearchCard, |     DictAttackStateRead, | ||||||
|     DictAttackStateSearchKeys, |  | ||||||
|     DictAttackStateCardRemoved, |     DictAttackStateCardRemoved, | ||||||
|     DictAttackStateSuccess, |  | ||||||
|     DictAttackStateFail, |  | ||||||
| } DictAttackState; | } DictAttackState; | ||||||
| 
 | 
 | ||||||
| struct DictAttack { | struct DictAttack { | ||||||
|     View* view; |     View* view; | ||||||
|     DictAttackResultCallback callback; |     DictAttackCallback callback; | ||||||
|     void* context; |     void* context; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     DictAttackState state; |     DictAttackState state; | ||||||
|     MfClassicType type; |     MfClassicType type; | ||||||
|     uint8_t current_sector; |     string_t header; | ||||||
|     uint8_t total_sectors; |     uint8_t sectors_total; | ||||||
|     uint8_t keys_a_found; |     uint8_t sectors_read; | ||||||
|     uint8_t keys_a_total; |     uint8_t sector_current; | ||||||
|     uint8_t keys_b_found; |     uint8_t keys_total; | ||||||
|     uint8_t keys_b_total; |     uint8_t keys_found; | ||||||
| } DictAttackViewModel; | } DictAttackViewModel; | ||||||
| 
 | 
 | ||||||
| static void dict_attack_draw_callback(Canvas* canvas, void* model) { | static void dict_attack_draw_callback(Canvas* canvas, void* model) { | ||||||
|     DictAttackViewModel* m = model; |     DictAttackViewModel* m = model; | ||||||
|     if(m->state == DictAttackStateSearchCard) { |     if(m->state == DictAttackStateCardRemoved) { | ||||||
|         canvas_set_font(canvas, FontPrimary); |         canvas_set_font(canvas, FontPrimary); | ||||||
|         canvas_draw_str_aligned( |         canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); | ||||||
|             canvas, 64, 32, AlignCenter, AlignCenter, "Detecting Mifare Classic"); |  | ||||||
|     } else if(m->state == DictAttackStateCardRemoved) { |  | ||||||
|         canvas_set_font(canvas, FontPrimary); |  | ||||||
|         canvas_draw_str_aligned( |  | ||||||
|             canvas, 64, 32, AlignCenter, AlignTop, "Place card back to flipper"); |  | ||||||
|     } else { |  | ||||||
|         char draw_str[32]; |  | ||||||
|         if(m->state == DictAttackStateSearchKeys) { |  | ||||||
|             snprintf( |  | ||||||
|                 draw_str, sizeof(draw_str), "Searching keys for sector %d", m->current_sector); |  | ||||||
|             canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, draw_str); |  | ||||||
|         } else if(m->state == DictAttackStateSuccess) { |  | ||||||
|             canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Complete!"); |  | ||||||
|             elements_button_right(canvas, "More"); |  | ||||||
|         } else if(m->state == DictAttackStateFail) { |  | ||||||
|             canvas_draw_str_aligned( |  | ||||||
|                 canvas, 64, 2, AlignCenter, AlignTop, "Failed to read any sector"); |  | ||||||
|         } |  | ||||||
|         uint16_t keys_found = m->keys_a_found + m->keys_b_found; |  | ||||||
|         uint16_t keys_total = m->keys_a_total + m->keys_b_total; |  | ||||||
|         float progress = (float)(m->current_sector) / (float)(m->total_sectors); |  | ||||||
|         elements_progress_bar(canvas, 5, 12, 120, progress); |  | ||||||
|         canvas_set_font(canvas, FontSecondary); |         canvas_set_font(canvas, FontSecondary); | ||||||
|         snprintf(draw_str, sizeof(draw_str), "Total keys found: %d/%d", keys_found, keys_total); |         elements_multiline_text_aligned( | ||||||
|         canvas_draw_str_aligned(canvas, 1, 23, AlignLeft, AlignTop, draw_str); |             canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); | ||||||
|  |     } else if(m->state == DictAttackStateRead) { | ||||||
|  |         char draw_str[32] = {}; | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, string_get_cstr(m->header)); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         float progress = | ||||||
|  |             m->sectors_total == 0 ? 0 : (float)(m->sector_current) / (float)(m->sectors_total); | ||||||
|  |         elements_progress_bar(canvas, 5, 15, 120, progress); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); | ||||||
|  |         canvas_draw_str_aligned(canvas, 1, 28, AlignLeft, AlignTop, draw_str); | ||||||
|         snprintf( |         snprintf( | ||||||
|             draw_str, sizeof(draw_str), "A keys found: %d/%d", m->keys_a_found, m->keys_a_total); |             draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); | ||||||
|         canvas_draw_str_aligned(canvas, 1, 34, AlignLeft, AlignTop, draw_str); |         canvas_draw_str_aligned(canvas, 1, 40, AlignLeft, AlignTop, draw_str); | ||||||
|         snprintf( |  | ||||||
|             draw_str, sizeof(draw_str), "B keys found: %d/%d", m->keys_b_found, m->keys_b_total); |  | ||||||
|         canvas_draw_str_aligned(canvas, 1, 45, AlignLeft, AlignTop, draw_str); |  | ||||||
|     } |     } | ||||||
|  |     elements_button_center(canvas, "Skip"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool dict_attack_input_callback(InputEvent* event, void* context) { | static bool dict_attack_input_callback(InputEvent* event, void* context) { | ||||||
|     DictAttack* dict_attack = context; |     DictAttack* dict_attack = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     DictAttackState state; |     if(event->type == InputTypeShort && event->key == InputKeyOk) { | ||||||
|     with_view_model( |  | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |  | ||||||
|             state = model->state; |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
|     if(state == DictAttackStateSuccess && event->type == InputTypeShort && |  | ||||||
|        event->key == InputKeyRight) { |  | ||||||
|         if(dict_attack->callback) { |         if(dict_attack->callback) { | ||||||
|             dict_attack->callback(dict_attack->context); |             dict_attack->callback(dict_attack->context); | ||||||
|         } |         } | ||||||
| @ -93,11 +70,21 @@ DictAttack* dict_attack_alloc() { | |||||||
|     view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); |     view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); | ||||||
|     view_set_input_callback(dict_attack->view, dict_attack_input_callback); |     view_set_input_callback(dict_attack->view, dict_attack_input_callback); | ||||||
|     view_set_context(dict_attack->view, dict_attack); |     view_set_context(dict_attack->view, dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             string_init(model->header); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|     return dict_attack; |     return dict_attack; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dict_attack_free(DictAttack* dict_attack) { | void dict_attack_free(DictAttack* dict_attack) { | ||||||
|     furi_assert(dict_attack); |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             string_clear(model->header); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|     view_free(dict_attack->view); |     view_free(dict_attack->view); | ||||||
|     free(dict_attack); |     free(dict_attack); | ||||||
| } | } | ||||||
| @ -106,8 +93,15 @@ void dict_attack_reset(DictAttack* dict_attack) { | |||||||
|     furi_assert(dict_attack); |     furi_assert(dict_attack); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|             memset(model, 0, sizeof(DictAttackViewModel)); |             model->state = DictAttackStateRead; | ||||||
|             return true; |             model->type = MfClassicType1k; | ||||||
|  |             model->sectors_total = 0; | ||||||
|  |             model->sectors_read = 0; | ||||||
|  |             model->sector_current = 0; | ||||||
|  |             model->keys_total = 0; | ||||||
|  |             model->keys_found = 0; | ||||||
|  |             string_reset(model->header); | ||||||
|  |             return false; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -116,78 +110,88 @@ View* dict_attack_get_view(DictAttack* dict_attack) { | |||||||
|     return dict_attack->view; |     return dict_attack->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dict_attack_set_result_callback( | void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { | ||||||
|     DictAttack* dict_attack, |  | ||||||
|     DictAttackResultCallback callback, |  | ||||||
|     void* context) { |  | ||||||
|     furi_assert(dict_attack); |     furi_assert(dict_attack); | ||||||
|     furi_assert(callback); |     furi_assert(callback); | ||||||
|     dict_attack->callback = callback; |     dict_attack->callback = callback; | ||||||
|     dict_attack->context = context; |     dict_attack->context = context; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dict_attack_card_detected(DictAttack* dict_attack, MfClassicType type) { | void dict_attack_set_header(DictAttack* dict_attack, const char* header) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     furi_assert(header); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             string_set_str(model->header, header); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { | ||||||
|     furi_assert(dict_attack); |     furi_assert(dict_attack); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|             model->state = DictAttackStateSearchKeys; |             model->state = DictAttackStateRead; | ||||||
|             if(type == MfClassicType1k) { |             model->sectors_total = mf_classic_get_total_sectors_num(type); | ||||||
|                 model->total_sectors = 16; |             model->keys_total = model->sectors_total * 2; | ||||||
|                 model->keys_a_total = 16; |             return true; | ||||||
|                 model->keys_b_total = 16; |         }); | ||||||
|             } else if(type == MfClassicType4k) { | } | ||||||
|                 model->total_sectors = 40; | 
 | ||||||
|                 model->keys_a_total = 40; | void dict_attack_set_card_removed(DictAttack* dict_attack) { | ||||||
|                 model->keys_b_total = 40; |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             model->state = DictAttackStateCardRemoved; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             model->sectors_read = sec_read; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             model->keys_found = keys_found; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             model->sector_current = curr_sec; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_current_sector(DictAttack* dict_attack) { | ||||||
|  |     furi_assert(dict_attack); | ||||||
|  |     with_view_model( | ||||||
|  |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|  |             if(model->sector_current < model->sectors_total) { | ||||||
|  |                 model->sector_current++; | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dict_attack_card_removed(DictAttack* dict_attack) { | void dict_attack_inc_keys_found(DictAttack* dict_attack) { | ||||||
|     furi_assert(dict_attack); |     furi_assert(dict_attack); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |         dict_attack->view, (DictAttackViewModel * model) { | ||||||
|             if(model->state == DictAttackStateSearchKeys) { |             if(model->keys_found < model->keys_total) { | ||||||
|                 model->state = DictAttackStateCardRemoved; |                 model->keys_found++; | ||||||
|             } else { |  | ||||||
|                 model->state = DictAttackStateSearchCard; |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void dict_attack_inc_curr_sector(DictAttack* dict_attack) { |  | ||||||
|     furi_assert(dict_attack); |  | ||||||
|     with_view_model( |  | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |  | ||||||
|             model->current_sector++; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void dict_attack_inc_found_key(DictAttack* dict_attack, MfClassicKey key) { |  | ||||||
|     furi_assert(dict_attack); |  | ||||||
|     with_view_model( |  | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |  | ||||||
|             model->state = DictAttackStateSearchKeys; |  | ||||||
|             if(key == MfClassicKeyA) { |  | ||||||
|                 model->keys_a_found++; |  | ||||||
|             } else if(key == MfClassicKeyB) { |  | ||||||
|                 model->keys_b_found++; |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void dict_attack_set_result(DictAttack* dict_attack, bool success) { |  | ||||||
|     furi_assert(dict_attack); |  | ||||||
|     with_view_model( |  | ||||||
|         dict_attack->view, (DictAttackViewModel * model) { |  | ||||||
|             if(success) { |  | ||||||
|                 model->state = DictAttackStateSuccess; |  | ||||||
|             } else { |  | ||||||
|                 model->state = DictAttackStateFail; |  | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -3,11 +3,11 @@ | |||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
| 
 | 
 | ||||||
| #include <lib/nfc_protocols/mifare_classic.h> | #include <lib/nfc/protocols/mifare_classic.h> | ||||||
| 
 | 
 | ||||||
| typedef struct DictAttack DictAttack; | typedef struct DictAttack DictAttack; | ||||||
| 
 | 
 | ||||||
| typedef void (*DictAttackResultCallback)(void* context); | typedef void (*DictAttackCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| DictAttack* dict_attack_alloc(); | DictAttack* dict_attack_alloc(); | ||||||
| 
 | 
 | ||||||
| @ -17,17 +17,20 @@ void dict_attack_reset(DictAttack* dict_attack); | |||||||
| 
 | 
 | ||||||
| View* dict_attack_get_view(DictAttack* dict_attack); | View* dict_attack_get_view(DictAttack* dict_attack); | ||||||
| 
 | 
 | ||||||
| void dict_attack_set_result_callback( | void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); | ||||||
|     DictAttack* dict_attack, |  | ||||||
|     DictAttackResultCallback callback, |  | ||||||
|     void* context); |  | ||||||
| 
 | 
 | ||||||
| void dict_attack_card_detected(DictAttack* dict_attack, MfClassicType type); | void dict_attack_set_header(DictAttack* dict_attack, const char* header); | ||||||
| 
 | 
 | ||||||
| void dict_attack_card_removed(DictAttack* dict_attack); | void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); | ||||||
| 
 | 
 | ||||||
| void dict_attack_inc_curr_sector(DictAttack* dict_attack); | void dict_attack_set_card_removed(DictAttack* dict_attack); | ||||||
| 
 | 
 | ||||||
| void dict_attack_inc_found_key(DictAttack* dict_attack, MfClassicKey key); | void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); | ||||||
| 
 | 
 | ||||||
| void dict_attack_set_result(DictAttack* dict_attack, bool success); | void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); | ||||||
|  | 
 | ||||||
|  | void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_current_sector(DictAttack* dict_attack); | ||||||
|  | 
 | ||||||
|  | void dict_attack_inc_keys_found(DictAttack* dict_attack); | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <applications/storage/storage.h> | #include <applications/storage/storage.h> | ||||||
| #include <lib/flipper_format/flipper_format.h> | #include <lib/flipper_format/flipper_format.h> | ||||||
| #include <lib/nfc_protocols/nfca.h> | #include <lib/nfc/protocols/nfca.h> | ||||||
| #include <lib/digital_signal/digital_signal.h> | #include <lib/digital_signal/digital_signal.h> | ||||||
| 
 | 
 | ||||||
| #include <lib/flipper_format/flipper_format_i.h> | #include <lib/flipper_format/flipper_format_i.h> | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/NFC/NFC_manual.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/NFC/NFC_manual.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/NFC/Reader_detect.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/NFC/Reader_detect.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
| @ -186,6 +186,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( | |||||||
|         "subghz", |         "subghz", | ||||||
|         "flipperformat", |         "flipperformat", | ||||||
|         "toolbox", |         "toolbox", | ||||||
|  |         "nfc", | ||||||
|         "microtar", |         "microtar", | ||||||
|         "usb_stm32", |         "usb_stm32", | ||||||
|         "st25rfal002", |         "st25rfal002", | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| #include <lib/nfc_protocols/nfca.h> | #include <lib/nfc/protocols/nfca.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
|  | |||||||
| @ -88,6 +88,10 @@ extern "C" { | |||||||
| #define FURI_BIT(x, n) (((x) >> (n)) & 1) | #define FURI_BIT(x, n) (((x) >> (n)) & 1) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifndef FURI_BIT_SET | ||||||
|  | #define FURI_BIT_SET(x, n) ((x) |= (1 << (n))) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifndef FURI_IS_IRQ_MASKED | #ifndef FURI_IS_IRQ_MASKED | ||||||
| #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) | #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -70,6 +70,7 @@ libs = env.BuildModules( | |||||||
|         "infrared", |         "infrared", | ||||||
|         "littlefs", |         "littlefs", | ||||||
|         "subghz", |         "subghz", | ||||||
|  |         "nfc", | ||||||
|         "appframe", |         "appframe", | ||||||
|         "misc", |         "misc", | ||||||
|         "mbedtls", |         "mbedtls", | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ env.Append( | |||||||
|         "#/lib/heatshrink", |         "#/lib/heatshrink", | ||||||
|         "#/lib/micro-ecc", |         "#/lib/micro-ecc", | ||||||
|         "#/lib/nanopb", |         "#/lib/nanopb", | ||||||
|         "#/lib/nfc_protocols", |  | ||||||
|         "#/lib/u8g2", |         "#/lib/u8g2", | ||||||
|     ], |     ], | ||||||
|     CPPDEFINES=[ |     CPPDEFINES=[ | ||||||
| @ -24,7 +23,6 @@ sources = [] | |||||||
| libs_recurse = [ | libs_recurse = [ | ||||||
|     "digital_signal", |     "digital_signal", | ||||||
|     "micro-ecc", |     "micro-ecc", | ||||||
|     "nfc_protocols", |  | ||||||
|     "one_wire", |     "one_wire", | ||||||
|     "u8g2", |     "u8g2", | ||||||
|     "update_util", |     "update_util", | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								lib/nfc/SConscript
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/nfc/SConscript
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | env.Append( | ||||||
|  |     CPPPATH=[ | ||||||
|  |         "#/lib/nfc", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | libenv = env.Clone(FW_LIB_NAME="nfc") | ||||||
|  | libenv.ApplyLibFlags() | ||||||
|  | 
 | ||||||
|  | sources = libenv.GlobRecursive("*.c*") | ||||||
|  | 
 | ||||||
|  | lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) | ||||||
|  | libenv.Install("${LIB_DIST_DIR}", lib) | ||||||
|  | Return("lib") | ||||||
							
								
								
									
										148
									
								
								lib/nfc/helpers/mf_classic_dict.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								lib/nfc/helpers/mf_classic_dict.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,148 @@ | |||||||
|  | #include "mf_classic_dict.h" | ||||||
|  | 
 | ||||||
|  | #include <lib/toolbox/args.h> | ||||||
|  | #include <lib/flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | #define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") | ||||||
|  | #define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc") | ||||||
|  | 
 | ||||||
|  | #define TAG "MfClassicDict" | ||||||
|  | 
 | ||||||
|  | #define NFC_MF_CLASSIC_KEY_LEN (13) | ||||||
|  | 
 | ||||||
|  | struct MfClassicDict { | ||||||
|  |     Stream* stream; | ||||||
|  |     uint32_t total_keys; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_check_presence(MfClassicDictType dict_type) { | ||||||
|  |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     bool dict_present = false; | ||||||
|  |     if(dict_type == MfClassicDictTypeFlipper) { | ||||||
|  |         dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; | ||||||
|  |     } else if(dict_type == MfClassicDictTypeUser) { | ||||||
|  |         dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     return dict_present; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { | ||||||
|  |     MfClassicDict* dict = malloc(sizeof(MfClassicDict)); | ||||||
|  |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     dict->stream = buffered_file_stream_alloc(storage); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     bool dict_loaded = false; | ||||||
|  |     do { | ||||||
|  |         if(dict_type == MfClassicDictTypeFlipper) { | ||||||
|  |             if(!buffered_file_stream_open( | ||||||
|  |                    dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |                 buffered_file_stream_close(dict->stream); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if(dict_type == MfClassicDictTypeUser) { | ||||||
|  |             if(!buffered_file_stream_open( | ||||||
|  |                    dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { | ||||||
|  |                 buffered_file_stream_close(dict->stream); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Read total amount of keys
 | ||||||
|  |         string_t next_line; | ||||||
|  |         string_init(next_line); | ||||||
|  |         while(true) { | ||||||
|  |             if(!stream_read_line(dict->stream, next_line)) break; | ||||||
|  |             if(string_get_char(next_line, 0) == '#') continue; | ||||||
|  |             if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; | ||||||
|  |             dict->total_keys++; | ||||||
|  |         } | ||||||
|  |         string_clear(next_line); | ||||||
|  |         stream_rewind(dict->stream); | ||||||
|  | 
 | ||||||
|  |         dict_loaded = true; | ||||||
|  |         FURI_LOG_I(TAG, "Loaded dictionary with %d keys", dict->total_keys); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     if(!dict_loaded) { | ||||||
|  |         buffered_file_stream_close(dict->stream); | ||||||
|  |         free(dict); | ||||||
|  |         dict = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return dict; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_classic_dict_free(MfClassicDict* dict) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     buffered_file_stream_close(dict->stream); | ||||||
|  |     stream_free(dict->stream); | ||||||
|  |     free(dict); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) { | ||||||
|  |     furi_assert(dict); | ||||||
|  | 
 | ||||||
|  |     return dict->total_keys; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     uint8_t key_byte_tmp = 0; | ||||||
|  |     string_t next_line; | ||||||
|  |     string_init(next_line); | ||||||
|  | 
 | ||||||
|  |     bool key_read = false; | ||||||
|  |     *key = 0ULL; | ||||||
|  |     while(!key_read) { | ||||||
|  |         if(!stream_read_line(dict->stream, next_line)) break; | ||||||
|  |         if(string_get_char(next_line, 0) == '#') continue; | ||||||
|  |         if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; | ||||||
|  |         for(uint8_t i = 0; i < 12; i += 2) { | ||||||
|  |             args_char_to_hex( | ||||||
|  |                 string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); | ||||||
|  |             *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); | ||||||
|  |         } | ||||||
|  |         key_read = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(next_line); | ||||||
|  |     return key_read; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_rewind(MfClassicDict* dict) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     return stream_rewind(dict->stream); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     string_t key_str; | ||||||
|  |     string_init(key_str); | ||||||
|  |     for(size_t i = 0; i < 6; i++) { | ||||||
|  |         string_cat_printf(key_str, "%02X", key[i]); | ||||||
|  |     } | ||||||
|  |     string_cat_printf(key_str, "\n"); | ||||||
|  | 
 | ||||||
|  |     bool key_added = false; | ||||||
|  |     do { | ||||||
|  |         if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; | ||||||
|  |         if(!stream_insert_string(dict->stream, key_str)) break; | ||||||
|  |         key_added = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(key_str); | ||||||
|  |     return key_added; | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								lib/nfc/helpers/mf_classic_dict.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/nfc/helpers/mf_classic_dict.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <lib/flipper_format/flipper_format.h> | ||||||
|  | #include <lib/toolbox/stream/file_stream.h> | ||||||
|  | #include <lib/toolbox/stream/buffered_file_stream.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     MfClassicDictTypeUser, | ||||||
|  |     MfClassicDictTypeFlipper, | ||||||
|  | } MfClassicDictType; | ||||||
|  | 
 | ||||||
|  | typedef struct MfClassicDict MfClassicDict; | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_check_presence(MfClassicDictType dict_type); | ||||||
|  | 
 | ||||||
|  | MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type); | ||||||
|  | 
 | ||||||
|  | void mf_classic_dict_free(MfClassicDict* dict); | ||||||
|  | 
 | ||||||
|  | uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_rewind(MfClassicDict* dict); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key); | ||||||
| @ -3,20 +3,29 @@ | |||||||
| #include "m-string.h" | #include "m-string.h" | ||||||
| #include "nfc_types.h" | #include "nfc_types.h" | ||||||
| 
 | 
 | ||||||
| #include <toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
|  | #include <lib/toolbox/hex.h> | ||||||
|  | #include <lib/nfc/protocols/nfc_util.h> | ||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
| 
 | 
 | ||||||
|  | #define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache") | ||||||
|  | #define NFC_DEVICE_KEYS_EXTENSION ".keys" | ||||||
|  | 
 | ||||||
| static const char* nfc_file_header = "Flipper NFC device"; | static const char* nfc_file_header = "Flipper NFC device"; | ||||||
| static const uint32_t nfc_file_version = 2; | static const uint32_t nfc_file_version = 2; | ||||||
| 
 | 
 | ||||||
|  | static const char* nfc_keys_file_header = "Flipper NFC keys"; | ||||||
|  | static const uint32_t nfc_keys_file_version = 1; | ||||||
|  | 
 | ||||||
| // Protocols format versions
 | // Protocols format versions
 | ||||||
| static const uint32_t nfc_mifare_classic_data_format_version = 1; | static const uint32_t nfc_mifare_classic_data_format_version = 2; | ||||||
| 
 | 
 | ||||||
| NfcDevice* nfc_device_alloc() { | NfcDevice* nfc_device_alloc() { | ||||||
|     NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); |     NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); | ||||||
|     nfc_dev->storage = furi_record_open(RECORD_STORAGE); |     nfc_dev->storage = furi_record_open(RECORD_STORAGE); | ||||||
|     nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); |     nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); | ||||||
|     string_init(nfc_dev->load_path); |     string_init(nfc_dev->load_path); | ||||||
|  |     string_init(nfc_dev->dev_data.parsed_data); | ||||||
|     return nfc_dev; |     return nfc_dev; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -26,6 +35,7 @@ void nfc_device_free(NfcDevice* nfc_dev) { | |||||||
|     furi_record_close(RECORD_STORAGE); |     furi_record_close(RECORD_STORAGE); | ||||||
|     furi_record_close(RECORD_DIALOGS); |     furi_record_close(RECORD_DIALOGS); | ||||||
|     string_clear(nfc_dev->load_path); |     string_clear(nfc_dev->load_path); | ||||||
|  |     string_clear(nfc_dev->dev_data.parsed_data); | ||||||
|     free(nfc_dev); |     free(nfc_dev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -648,6 +658,52 @@ bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | |||||||
|     return parsed; |     return parsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void nfc_device_write_mifare_classic_block( | ||||||
|  |     string_t block_str, | ||||||
|  |     MfClassicData* data, | ||||||
|  |     uint8_t block_num) { | ||||||
|  |     string_reset(block_str); | ||||||
|  |     bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); | ||||||
|  |     if(is_sec_trailer) { | ||||||
|  |         uint8_t sector_num = mf_classic_get_sector_by_block(block_num); | ||||||
|  |         MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); | ||||||
|  |         // Write key A
 | ||||||
|  |         for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { | ||||||
|  |             if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { | ||||||
|  |                 string_cat_printf(block_str, "%02X ", sec_tr->key_a[i]); | ||||||
|  |             } else { | ||||||
|  |                 string_cat_printf(block_str, "?? "); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // Write Access bytes
 | ||||||
|  |         for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { | ||||||
|  |             if(mf_classic_is_block_read(data, block_num)) { | ||||||
|  |                 string_cat_printf(block_str, "%02X ", sec_tr->access_bits[i]); | ||||||
|  |             } else { | ||||||
|  |                 string_cat_printf(block_str, "?? "); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // Write key B
 | ||||||
|  |         for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { | ||||||
|  |             if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { | ||||||
|  |                 string_cat_printf(block_str, "%02X ", sec_tr->key_b[i]); | ||||||
|  |             } else { | ||||||
|  |                 string_cat_printf(block_str, "?? "); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         // Write data block
 | ||||||
|  |         for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { | ||||||
|  |             if(mf_classic_is_block_read(data, block_num)) { | ||||||
|  |                 string_cat_printf(block_str, "%02X ", data->block[block_num].value[i]); | ||||||
|  |             } else { | ||||||
|  |                 string_cat_printf(block_str, "?? "); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     string_strim(block_str); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { | static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|     bool saved = false; |     bool saved = false; | ||||||
|     MfClassicData* data = &dev->dev_data.mf_classic_data; |     MfClassicData* data = &dev->dev_data.mf_classic_data; | ||||||
| @ -669,23 +725,21 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* | |||||||
|         if(!flipper_format_write_uint32( |         if(!flipper_format_write_uint32( | ||||||
|                file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) |                file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) | ||||||
|             break; |             break; | ||||||
| 
 |  | ||||||
|         if(!flipper_format_write_comment_cstr( |         if(!flipper_format_write_comment_cstr( | ||||||
|                file, "Key map is the bit mask indicating valid key in each sector")) |                file, "Mifare Classic blocks, \'??\' means unknown data")) | ||||||
|             break; |             break; | ||||||
|         if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; |  | ||||||
|         if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; |  | ||||||
| 
 |  | ||||||
|         if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break; |  | ||||||
|         bool block_saved = true; |         bool block_saved = true; | ||||||
|  |         string_t block_str; | ||||||
|  |         string_init(block_str); | ||||||
|         for(size_t i = 0; i < blocks; i++) { |         for(size_t i = 0; i < blocks; i++) { | ||||||
|             string_printf(temp_str, "Block %d", i); |             string_printf(temp_str, "Block %d", i); | ||||||
|             if(!flipper_format_write_hex( |             nfc_device_write_mifare_classic_block(block_str, data, i); | ||||||
|                    file, string_get_cstr(temp_str), data->block[i].value, 16)) { |             if(!flipper_format_write_string(file, string_get_cstr(temp_str), block_str)) { | ||||||
|                 block_saved = false; |                 block_saved = false; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         string_clear(block_str); | ||||||
|         if(!block_saved) break; |         if(!block_saved) break; | ||||||
|         saved = true; |         saved = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| @ -694,6 +748,59 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* | |||||||
|     return saved; |     return saved; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void nfc_device_load_mifare_classic_block( | ||||||
|  |     string_t block_str, | ||||||
|  |     MfClassicData* data, | ||||||
|  |     uint8_t block_num) { | ||||||
|  |     string_strim(block_str); | ||||||
|  |     MfClassicBlock block_tmp = {}; | ||||||
|  |     bool is_sector_trailer = mf_classic_is_sector_trailer(block_num); | ||||||
|  |     uint8_t sector_num = mf_classic_get_sector_by_block(block_num); | ||||||
|  |     uint16_t block_unknown_bytes_mask = 0; | ||||||
|  | 
 | ||||||
|  |     string_strim(block_str); | ||||||
|  |     for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { | ||||||
|  |         char hi = string_get_char(block_str, 3 * i); | ||||||
|  |         char low = string_get_char(block_str, 3 * i + 1); | ||||||
|  |         uint8_t byte = 0; | ||||||
|  |         if(hex_chars_to_uint8(hi, low, &byte)) { | ||||||
|  |             block_tmp.value[i] = byte; | ||||||
|  |         } else { | ||||||
|  |             FURI_BIT_SET(block_unknown_bytes_mask, i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(block_unknown_bytes_mask == 0xffff) { | ||||||
|  |         // All data is unknown, exit
 | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(is_sector_trailer) { | ||||||
|  |         MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; | ||||||
|  |         // Load Key A
 | ||||||
|  |         // Key A mask 0b0000000000111111 = 0x003f
 | ||||||
|  |         if((block_unknown_bytes_mask & 0x003f) == 0) { | ||||||
|  |             uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a, sizeof(sec_tr_tmp->key_a)); | ||||||
|  |             mf_classic_set_key_found(data, sector_num, MfClassicKeyA, key); | ||||||
|  |         } | ||||||
|  |         // Load Access Bits
 | ||||||
|  |         // Access bits mask 0b0000001111000000 = 0x03c0
 | ||||||
|  |         if((block_unknown_bytes_mask & 0x03c0) == 0) { | ||||||
|  |             mf_classic_set_block_read(data, block_num, &block_tmp); | ||||||
|  |         } | ||||||
|  |         // Load Key B
 | ||||||
|  |         // Key B mask 0b1111110000000000 = 0xfc00
 | ||||||
|  |         if((block_unknown_bytes_mask & 0xfc00) == 0) { | ||||||
|  |             uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b, sizeof(sec_tr_tmp->key_b)); | ||||||
|  |             mf_classic_set_key_found(data, sector_num, MfClassicKeyB, key); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(block_unknown_bytes_mask == 0) { | ||||||
|  |             mf_classic_set_block_read(data, block_num, &block_tmp); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { | static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { | ||||||
|     bool parsed = false; |     bool parsed = false; | ||||||
|     MfClassicData* data = &dev->dev_data.mf_classic_data; |     MfClassicData* data = &dev->dev_data.mf_classic_data; | ||||||
| @ -701,6 +808,7 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* | |||||||
|     uint32_t data_format_version = 0; |     uint32_t data_format_version = 0; | ||||||
|     string_init(temp_str); |     string_init(temp_str); | ||||||
|     uint16_t data_blocks = 0; |     uint16_t data_blocks = 0; | ||||||
|  |     memset(data, 0, sizeof(MfClassicData)); | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         // Read Mifare Classic type
 |         // Read Mifare Classic type
 | ||||||
| @ -715,29 +823,40 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         bool old_format = false; | ||||||
|         // Read Mifare Classic format version
 |         // Read Mifare Classic format version
 | ||||||
|         if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { |         if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { | ||||||
|             // Load unread sectors with zero keys access for backward compatability
 |             // Load unread sectors with zero keys access for backward compatability
 | ||||||
|             if(!flipper_format_rewind(file)) break; |             if(!flipper_format_rewind(file)) break; | ||||||
|             data->key_a_mask = 0xffffffffffffffff; |             old_format = true; | ||||||
|             data->key_b_mask = 0xffffffffffffffff; |  | ||||||
|         } else { |         } else { | ||||||
|             if(data_format_version != nfc_mifare_classic_data_format_version) break; |             if(data_format_version < nfc_mifare_classic_data_format_version) { | ||||||
|             if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; |                 old_format = true; | ||||||
|             if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Read Mifare Classic blocks
 |         // Read Mifare Classic blocks
 | ||||||
|         bool block_read = true; |         bool block_read = true; | ||||||
|  |         string_t block_str; | ||||||
|  |         string_init(block_str); | ||||||
|         for(size_t i = 0; i < data_blocks; i++) { |         for(size_t i = 0; i < data_blocks; i++) { | ||||||
|             string_printf(temp_str, "Block %d", i); |             string_printf(temp_str, "Block %d", i); | ||||||
|             if(!flipper_format_read_hex( |             if(!flipper_format_read_string(file, string_get_cstr(temp_str), block_str)) { | ||||||
|                    file, string_get_cstr(temp_str), data->block[i].value, 16)) { |  | ||||||
|                 block_read = false; |                 block_read = false; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |             nfc_device_load_mifare_classic_block(block_str, data, i); | ||||||
|         } |         } | ||||||
|  |         string_clear(block_str); | ||||||
|         if(!block_read) break; |         if(!block_read) break; | ||||||
|  | 
 | ||||||
|  |         // Set keys and blocks as unknown for backward compatibility
 | ||||||
|  |         if(old_format) { | ||||||
|  |             data->key_a_mask = 0ULL; | ||||||
|  |             data->key_b_mask = 0ULL; | ||||||
|  |             memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         parsed = true; |         parsed = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
| @ -745,6 +864,113 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* | |||||||
|     return parsed; |     return parsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void nfc_device_get_key_cache_file_path(NfcDevice* dev, string_t file_path) { | ||||||
|  |     uint8_t* uid = dev->dev_data.nfc_data.uid; | ||||||
|  |     uint8_t uid_len = dev->dev_data.nfc_data.uid_len; | ||||||
|  |     string_set_str(file_path, NFC_DEVICE_KEYS_FOLDER "/"); | ||||||
|  |     for(size_t i = 0; i < uid_len; i++) { | ||||||
|  |         string_cat_printf(file_path, "%02X", uid[i]); | ||||||
|  |     } | ||||||
|  |     string_cat_printf(file_path, NFC_DEVICE_KEYS_EXTENSION); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { | ||||||
|  |     FlipperFormat* file = flipper_format_file_alloc(dev->storage); | ||||||
|  |     MfClassicData* data = &dev->dev_data.mf_classic_data; | ||||||
|  |     string_t temp_str; | ||||||
|  |     string_init(temp_str); | ||||||
|  | 
 | ||||||
|  |     nfc_device_get_key_cache_file_path(dev, temp_str); | ||||||
|  |     bool save_success = false; | ||||||
|  |     do { | ||||||
|  |         if(!storage_simply_mkdir(dev->storage, NFC_DEVICE_KEYS_FOLDER)) break; | ||||||
|  |         if(!storage_simply_remove(dev->storage, string_get_cstr(temp_str))) break; | ||||||
|  |         if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; | ||||||
|  |         if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version)) | ||||||
|  |             break; | ||||||
|  |         if(data->type == MfClassicType1k) { | ||||||
|  |             if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; | ||||||
|  |         } else if(data->type == MfClassicType4k) { | ||||||
|  |             if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; | ||||||
|  |         if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; | ||||||
|  |         uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); | ||||||
|  |         bool key_save_success = true; | ||||||
|  |         for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { | ||||||
|  |             MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); | ||||||
|  |             if(FURI_BIT(data->key_a_mask, i)) { | ||||||
|  |                 string_printf(temp_str, "Key A sector %d", i); | ||||||
|  |                 key_save_success = | ||||||
|  |                     flipper_format_write_hex(file, string_get_cstr(temp_str), sec_tr->key_a, 6); | ||||||
|  |             } | ||||||
|  |             if(!key_save_success) break; | ||||||
|  |             if(FURI_BIT(data->key_a_mask, i)) { | ||||||
|  |                 string_printf(temp_str, "Key B sector %d", i); | ||||||
|  |                 key_save_success = | ||||||
|  |                     flipper_format_write_hex(file, string_get_cstr(temp_str), sec_tr->key_b, 6); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         save_success = key_save_success; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     flipper_format_free(file); | ||||||
|  |     string_clear(temp_str); | ||||||
|  |     return save_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_device_load_key_cache(NfcDevice* dev) { | ||||||
|  |     furi_assert(dev); | ||||||
|  |     string_t temp_str; | ||||||
|  |     string_init(temp_str); | ||||||
|  | 
 | ||||||
|  |     MfClassicData* data = &dev->dev_data.mf_classic_data; | ||||||
|  |     nfc_device_get_key_cache_file_path(dev, temp_str); | ||||||
|  |     FlipperFormat* file = flipper_format_file_alloc(dev->storage); | ||||||
|  | 
 | ||||||
|  |     bool load_success = false; | ||||||
|  |     do { | ||||||
|  |         if(storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) != FSE_OK) break; | ||||||
|  |         if(!flipper_format_file_open_existing(file, string_get_cstr(temp_str))) break; | ||||||
|  |         uint32_t version = 0; | ||||||
|  |         if(!flipper_format_read_header(file, temp_str, &version)) break; | ||||||
|  |         if(string_cmp_str(temp_str, nfc_keys_file_header)) break; | ||||||
|  |         if(version != nfc_keys_file_version) break; | ||||||
|  |         if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; | ||||||
|  |         if(!string_cmp_str(temp_str, "1K")) { | ||||||
|  |             data->type = MfClassicType1k; | ||||||
|  |         } else if(!string_cmp_str(temp_str, "4K")) { | ||||||
|  |             data->type = MfClassicType4k; | ||||||
|  |         } else { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; | ||||||
|  |         if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; | ||||||
|  |         uint8_t sectors = mf_classic_get_total_sectors_num(data->type); | ||||||
|  |         bool key_read_success = true; | ||||||
|  |         for(size_t i = 0; (i < sectors) && (key_read_success); i++) { | ||||||
|  |             MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); | ||||||
|  |             if(FURI_BIT(data->key_a_mask, i)) { | ||||||
|  |                 string_printf(temp_str, "Key A sector %d", i); | ||||||
|  |                 key_read_success = | ||||||
|  |                     flipper_format_read_hex(file, string_get_cstr(temp_str), sec_tr->key_a, 6); | ||||||
|  |             } | ||||||
|  |             if(!key_read_success) break; | ||||||
|  |             if(FURI_BIT(data->key_b_mask, i)) { | ||||||
|  |                 string_printf(temp_str, "Key B sector %d", i); | ||||||
|  |                 key_read_success = | ||||||
|  |                     flipper_format_read_hex(file, string_get_cstr(temp_str), sec_tr->key_b, 6); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         load_success = key_read_success; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(temp_str); | ||||||
|  |     flipper_format_free(file); | ||||||
|  | 
 | ||||||
|  |     return load_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nfc_device_set_name(NfcDevice* dev, const char* name) { | void nfc_device_set_name(NfcDevice* dev, const char* name) { | ||||||
|     furi_assert(dev); |     furi_assert(dev); | ||||||
| 
 | 
 | ||||||
| @ -815,7 +1041,10 @@ static bool nfc_device_save_file( | |||||||
|         } else if(dev->format == NfcDeviceSaveFormatBankCard) { |         } else if(dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|             if(!nfc_device_save_bank_card_data(file, dev)) break; |             if(!nfc_device_save_bank_card_data(file, dev)) break; | ||||||
|         } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { |         } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|  |             // Save data
 | ||||||
|             if(!nfc_device_save_mifare_classic_data(file, dev)) break; |             if(!nfc_device_save_mifare_classic_data(file, dev)) break; | ||||||
|  |             // Save keys cache
 | ||||||
|  |             if(!nfc_device_save_mifare_classic_keys(dev)) break; | ||||||
|         } |         } | ||||||
|         saved = true; |         saved = true; | ||||||
|     } while(0); |     } while(0); | ||||||
| @ -954,14 +1183,22 @@ bool nfc_file_select(NfcDevice* dev) { | |||||||
| void nfc_device_data_clear(NfcDeviceData* dev_data) { | void nfc_device_data_clear(NfcDeviceData* dev_data) { | ||||||
|     if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { |     if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         mf_df_clear(&dev_data->mf_df_data); |         mf_df_clear(&dev_data->mf_df_data); | ||||||
|  |     } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|  |         memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); | ||||||
|  |     } else if(dev_data->protocol == NfcDeviceProtocolMifareUl) { | ||||||
|  |         memset(&dev_data->mf_ul_data, 0, sizeof(MfUltralightData)); | ||||||
|  |     } else if(dev_data->protocol == NfcDeviceProtocolEMV) { | ||||||
|  |         memset(&dev_data->emv_data, 0, sizeof(EmvData)); | ||||||
|     } |     } | ||||||
|  |     memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); | ||||||
|  |     dev_data->protocol = NfcDeviceProtocolUnknown; | ||||||
|  |     string_reset(dev_data->parsed_data); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_device_clear(NfcDevice* dev) { | void nfc_device_clear(NfcDevice* dev) { | ||||||
|     furi_assert(dev); |     furi_assert(dev); | ||||||
| 
 | 
 | ||||||
|     nfc_device_data_clear(&dev->dev_data); |     nfc_device_data_clear(&dev->dev_data); | ||||||
|     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); |  | ||||||
|     dev->format = NfcDeviceSaveFormatUid; |     dev->format = NfcDeviceSaveFormatUid; | ||||||
|     string_reset(dev->load_path); |     string_reset(dev->load_path); | ||||||
| } | } | ||||||
| @ -6,10 +6,10 @@ | |||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include <furi_hal_nfc.h> | #include <furi_hal_nfc.h> | ||||||
| #include <lib/nfc_protocols/emv.h> | #include <lib/nfc/protocols/emv.h> | ||||||
| #include <lib/nfc_protocols/mifare_ultralight.h> | #include <lib/nfc/protocols/mifare_ultralight.h> | ||||||
| #include <lib/nfc_protocols/mifare_classic.h> | #include <lib/nfc/protocols/mifare_classic.h> | ||||||
| #include <lib/nfc_protocols/mifare_desfire.h> | #include <lib/nfc/protocols/mifare_desfire.h> | ||||||
| 
 | 
 | ||||||
| #define NFC_DEV_NAME_MAX_LEN 22 | #define NFC_DEV_NAME_MAX_LEN 22 | ||||||
| #define NFC_READER_DATA_MAX_SIZE 64 | #define NFC_READER_DATA_MAX_SIZE 64 | ||||||
| @ -51,6 +51,7 @@ typedef struct { | |||||||
|         MfClassicData mf_classic_data; |         MfClassicData mf_classic_data; | ||||||
|         MifareDesfireData mf_df_data; |         MifareDesfireData mf_df_data; | ||||||
|     }; |     }; | ||||||
|  |     string_t parsed_data; | ||||||
| } NfcDeviceData; | } NfcDeviceData; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -78,6 +79,8 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); | |||||||
| 
 | 
 | ||||||
| bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); | bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); | ||||||
| 
 | 
 | ||||||
|  | bool nfc_device_load_key_cache(NfcDevice* dev); | ||||||
|  | 
 | ||||||
| bool nfc_file_select(NfcDevice* dev); | bool nfc_file_select(NfcDevice* dev); | ||||||
| 
 | 
 | ||||||
| void nfc_device_data_clear(NfcDeviceData* dev); | void nfc_device_data_clear(NfcDeviceData* dev); | ||||||
							
								
								
									
										510
									
								
								lib/nfc/nfc_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										510
									
								
								lib/nfc/nfc_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,510 @@ | |||||||
|  | #include "nfc_worker_i.h" | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #include <platform.h> | ||||||
|  | #include "parsers/nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "NfcWorker" | ||||||
|  | 
 | ||||||
|  | /***************************** NFC Worker API *******************************/ | ||||||
|  | 
 | ||||||
|  | NfcWorker* nfc_worker_alloc() { | ||||||
|  |     NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); | ||||||
|  | 
 | ||||||
|  |     // Worker thread attributes
 | ||||||
|  |     nfc_worker->thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(nfc_worker->thread, "NfcWorker"); | ||||||
|  |     furi_thread_set_stack_size(nfc_worker->thread, 8192); | ||||||
|  |     furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); | ||||||
|  |     furi_thread_set_context(nfc_worker->thread, nfc_worker); | ||||||
|  | 
 | ||||||
|  |     nfc_worker->callback = NULL; | ||||||
|  |     nfc_worker->context = NULL; | ||||||
|  |     nfc_worker->storage = furi_record_open(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     // Initialize rfal
 | ||||||
|  |     while(furi_hal_nfc_is_busy()) { | ||||||
|  |         furi_delay_ms(10); | ||||||
|  |     } | ||||||
|  |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
|  | 
 | ||||||
|  |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |         nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return nfc_worker; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_free(NfcWorker* nfc_worker) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     furi_thread_free(nfc_worker->thread); | ||||||
|  | 
 | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); | ||||||
|  | 
 | ||||||
|  |     free(nfc_worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) { | ||||||
|  |     return nfc_worker->state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_start( | ||||||
|  |     NfcWorker* nfc_worker, | ||||||
|  |     NfcWorkerState state, | ||||||
|  |     NfcDeviceData* dev_data, | ||||||
|  |     NfcWorkerCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     furi_assert(dev_data); | ||||||
|  |     while(furi_hal_nfc_is_busy()) { | ||||||
|  |         furi_delay_ms(10); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     nfc_worker->callback = callback; | ||||||
|  |     nfc_worker->context = context; | ||||||
|  |     nfc_worker->dev_data = dev_data; | ||||||
|  |     nfc_worker_change_state(nfc_worker, state); | ||||||
|  |     furi_thread_start(nfc_worker->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_stop(NfcWorker* nfc_worker) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     furi_hal_nfc_stop(); | ||||||
|  |     nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); | ||||||
|  |     furi_thread_join(nfc_worker->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { | ||||||
|  |     nfc_worker->state = state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /***************************** NFC Worker Thread *******************************/ | ||||||
|  | 
 | ||||||
|  | int32_t nfc_worker_task(void* context) { | ||||||
|  |     NfcWorker* nfc_worker = context; | ||||||
|  | 
 | ||||||
|  |     furi_hal_nfc_exit_sleep(); | ||||||
|  | 
 | ||||||
|  |     if(nfc_worker->state == NfcWorkerStateRead) { | ||||||
|  |         nfc_worker_read(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { | ||||||
|  |         nfc_worker_emulate_uid(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||||
|  |         nfc_worker_emulate_apdu(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { | ||||||
|  |         nfc_worker_emulate_mf_ultralight(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { | ||||||
|  |         nfc_worker_emulate_mf_classic(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) { | ||||||
|  |         nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeUser); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack) { | ||||||
|  |         nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeFlipper); | ||||||
|  |     } | ||||||
|  |     furi_hal_nfc_sleep(); | ||||||
|  |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     bool read_success = false; | ||||||
|  |     MfUltralightReader reader = {}; | ||||||
|  |     MfUltralightData data = {}; | ||||||
|  | 
 | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||||
|  |     do { | ||||||
|  |         // Read card
 | ||||||
|  |         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; | ||||||
|  |         if(!mf_ul_read_card(tx_rx, &reader, &data)) break; | ||||||
|  |         // Copy data
 | ||||||
|  |         nfc_worker->dev_data->mf_ul_data = data; | ||||||
|  |         read_success = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return read_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker->callback); | ||||||
|  |     bool read_success = false; | ||||||
|  | 
 | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||||
|  |     do { | ||||||
|  |         // Try to read supported card
 | ||||||
|  |         FURI_LOG_I(TAG, "Try read supported card ..."); | ||||||
|  |         for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { | ||||||
|  |             if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|  |                 if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { | ||||||
|  |                     if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { | ||||||
|  |                         read_success = true; | ||||||
|  |                         nfc_supported_card[i].parse(nfc_worker); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(read_success) break; | ||||||
|  |         // Try to read card with key cache
 | ||||||
|  |         FURI_LOG_I(TAG, "Search for key cache ..."); | ||||||
|  |         if(nfc_worker->callback(NfcWorkerEventReadMfClassicLoadKeyCache, nfc_worker->context)) { | ||||||
|  |             FURI_LOG_I(TAG, "Load keys cache success. Start reading"); | ||||||
|  |             uint8_t sectors_read = | ||||||
|  |                 mf_classic_update_card(tx_rx, &nfc_worker->dev_data->mf_classic_data); | ||||||
|  |             uint8_t sectors_total = | ||||||
|  |                 mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type); | ||||||
|  |             FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total); | ||||||
|  |             read_success = (sectors_read == sectors_total); | ||||||
|  |         } | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return read_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     bool read_success = false; | ||||||
|  |     MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; | ||||||
|  | 
 | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||||
|  |     do { | ||||||
|  |         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; | ||||||
|  |         if(!mf_df_read_card(tx_rx, data)) break; | ||||||
|  |         read_success = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return read_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     bool read_success = false; | ||||||
|  |     EmvApplication emv_app = {}; | ||||||
|  |     EmvData* result = &nfc_worker->dev_data->emv_data; | ||||||
|  | 
 | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||||
|  |     do { | ||||||
|  |         // Read card
 | ||||||
|  |         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; | ||||||
|  |         if(!emv_read_bank_card(tx_rx, &emv_app)) break; | ||||||
|  |         // Copy data
 | ||||||
|  |         // TODO Set EmvData to reader or like in mifare ultralight!
 | ||||||
|  |         result->number_len = emv_app.card_number_len; | ||||||
|  |         memcpy(result->number, emv_app.card_number, result->number_len); | ||||||
|  |         result->aid_len = emv_app.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; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return read_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  | 
 | ||||||
|  |     bool card_read = false; | ||||||
|  |     furi_hal_nfc_sleep(); | ||||||
|  |     if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||||
|  |         FURI_LOG_I(TAG, "Mifare Ultralight / NTAG detected"); | ||||||
|  |         nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; | ||||||
|  |         card_read = nfc_worker_read_mf_ultralight(nfc_worker, tx_rx); | ||||||
|  |     } else if(mf_classic_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||||
|  |         FURI_LOG_I(TAG, "Mifare Classic detected"); | ||||||
|  |         nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; | ||||||
|  |         nfc_worker->dev_data->mf_classic_data.type = | ||||||
|  |             mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); | ||||||
|  |         card_read = nfc_worker_read_mf_classic(nfc_worker, tx_rx); | ||||||
|  |     } else if(mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||||
|  |         FURI_LOG_I(TAG, "Mifare DESFire detected"); | ||||||
|  |         nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; | ||||||
|  |         if(!nfc_worker_read_mf_desfire(nfc_worker, tx_rx)) { | ||||||
|  |             FURI_LOG_I(TAG, "Unknown card. Save UID"); | ||||||
|  |             nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; | ||||||
|  |         } | ||||||
|  |         card_read = true; | ||||||
|  |     } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { | ||||||
|  |         FURI_LOG_I(TAG, "ISO14443-4 card detected"); | ||||||
|  |         nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; | ||||||
|  |         if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) { | ||||||
|  |             FURI_LOG_I(TAG, "Unknown card. Save UID"); | ||||||
|  |             nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; | ||||||
|  |         } | ||||||
|  |         card_read = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return card_read; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_read(NfcWorker* nfc_worker) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     furi_assert(nfc_worker->callback); | ||||||
|  | 
 | ||||||
|  |     nfc_device_data_clear(nfc_worker->dev_data); | ||||||
|  |     NfcDeviceData* dev_data = nfc_worker->dev_data; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     FuriHalNfcTxRxContext tx_rx = {}; | ||||||
|  |     NfcWorkerEvent event = 0; | ||||||
|  |     bool card_not_detected_notified = false; | ||||||
|  | 
 | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateRead) { | ||||||
|  |         if(furi_hal_nfc_detect(nfc_data, 300)) { | ||||||
|  |             // Process first found device
 | ||||||
|  |             nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); | ||||||
|  |             card_not_detected_notified = false; | ||||||
|  |             if(nfc_data->type == FuriHalNfcTypeA) { | ||||||
|  |                 if(nfc_worker_read_nfca(nfc_worker, &tx_rx)) { | ||||||
|  |                     if(dev_data->protocol == NfcDeviceProtocolMifareUl) { | ||||||
|  |                         event = NfcWorkerEventReadMfUltralight; | ||||||
|  |                         break; | ||||||
|  |                     } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|  |                         event = NfcWorkerEventReadMfClassicDone; | ||||||
|  |                         break; | ||||||
|  |                     } else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|  |                         event = NfcWorkerEventReadMfDesfire; | ||||||
|  |                         break; | ||||||
|  |                     } else if(dev_data->protocol == NfcDeviceProtocolEMV) { | ||||||
|  |                         event = NfcWorkerEventReadBankCard; | ||||||
|  |                         break; | ||||||
|  |                     } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { | ||||||
|  |                         event = NfcWorkerEventReadUidNfcA; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|  |                         event = NfcWorkerEventReadMfClassicDictAttackRequired; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else if(nfc_data->type == FuriHalNfcTypeB) { | ||||||
|  |                 event = NfcWorkerEventReadUidNfcB; | ||||||
|  |                 break; | ||||||
|  |             } else if(nfc_data->type == FuriHalNfcTypeF) { | ||||||
|  |                 event = NfcWorkerEventReadUidNfcF; | ||||||
|  |                 break; | ||||||
|  |             } else if(nfc_data->type == FuriHalNfcTypeV) { | ||||||
|  |                 event = NfcWorkerEventReadUidNfcV; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(!card_not_detected_notified) { | ||||||
|  |                 nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); | ||||||
|  |                 card_not_detected_notified = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         furi_hal_nfc_sleep(); | ||||||
|  |         furi_delay_ms(100); | ||||||
|  |     } | ||||||
|  |     // Notify caller and exit
 | ||||||
|  |     if(event > NfcWorkerEventReserved) { | ||||||
|  |         nfc_worker->callback(event, nfc_worker->context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { | ||||||
|  |     FuriHalNfcTxRxContext tx_rx = {}; | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); | ||||||
|  |     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; | ||||||
|  | 
 | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateUidEmulate) { | ||||||
|  |         if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) { | ||||||
|  |             if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { | ||||||
|  |                 reader_data->size = tx_rx.rx_bits / 8; | ||||||
|  |                 if(reader_data->size > 0) { | ||||||
|  |                     memcpy(reader_data->data, tx_rx.rx_data, reader_data->size); | ||||||
|  |                     if(nfc_worker->callback) { | ||||||
|  |                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_E(TAG, "Failed to get reader commands"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||||
|  |     FuriHalNfcTxRxContext tx_rx = {}; | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); | ||||||
|  |     FuriHalNfcDevData params = { | ||||||
|  |         .uid = {0xCF, 0x72, 0xd4, 0x40}, | ||||||
|  |         .uid_len = 4, | ||||||
|  |         .atqa = {0x00, 0x04}, | ||||||
|  |         .sak = 0x20, | ||||||
|  |         .type = FuriHalNfcTypeA, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||||
|  |         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { | ||||||
|  |             FURI_LOG_D(TAG, "POS terminal detected"); | ||||||
|  |             if(emv_card_emulation(&tx_rx)) { | ||||||
|  |                 FURI_LOG_D(TAG, "EMV card emulated"); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             FURI_LOG_D(TAG, "Can't find reader"); | ||||||
|  |         } | ||||||
|  |         furi_hal_nfc_sleep(); | ||||||
|  |         furi_delay_ms(20); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     MfUltralightEmulator emulator = {}; | ||||||
|  |     mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { | ||||||
|  |         mf_ul_reset_emulation(&emulator, true); | ||||||
|  |         furi_hal_nfc_emulate_nfca( | ||||||
|  |             nfc_data->uid, | ||||||
|  |             nfc_data->uid_len, | ||||||
|  |             nfc_data->atqa, | ||||||
|  |             nfc_data->sak, | ||||||
|  |             mf_ul_prepare_emulation_response, | ||||||
|  |             &emulator, | ||||||
|  |             5000); | ||||||
|  |         // Check if data was modified
 | ||||||
|  |         if(emulator.data_changed) { | ||||||
|  |             nfc_worker->dev_data->mf_ul_data = emulator.data; | ||||||
|  |             if(nfc_worker->callback) { | ||||||
|  |                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|  |             } | ||||||
|  |             emulator.data_changed = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     furi_assert(nfc_worker->callback); | ||||||
|  | 
 | ||||||
|  |     MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; | ||||||
|  |     uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); | ||||||
|  |     uint64_t key = 0; | ||||||
|  |     FuriHalNfcTxRxContext tx_rx = {}; | ||||||
|  |     bool card_found_notified = true; | ||||||
|  |     bool card_removed_notified = false; | ||||||
|  | 
 | ||||||
|  |     // Load dictionary
 | ||||||
|  |     MfClassicDict* dict = mf_classic_dict_alloc(type); | ||||||
|  |     if(!dict) { | ||||||
|  |         FURI_LOG_E(TAG, "Dictionary not found"); | ||||||
|  |         nfc_worker->callback(NfcWorkerEventNoDictFound, nfc_worker->context); | ||||||
|  |         mf_classic_dict_free(dict); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Start Dictionary attack"); | ||||||
|  |     for(size_t i = 0; i < total_sectors; i++) { | ||||||
|  |         FURI_LOG_I(TAG, "Sector %d", i); | ||||||
|  |         nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); | ||||||
|  |         uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i); | ||||||
|  |         if(mf_classic_is_sector_read(data, i)) continue; | ||||||
|  |         bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); | ||||||
|  |         bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); | ||||||
|  |         while(mf_classic_dict_get_next_key(dict, &key)) { | ||||||
|  |             furi_hal_nfc_sleep(); | ||||||
|  |             if(furi_hal_nfc_activate_nfca(200, NULL)) { | ||||||
|  |                 furi_hal_nfc_sleep(); | ||||||
|  |                 if(!card_found_notified) { | ||||||
|  |                     nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); | ||||||
|  |                     card_found_notified = true; | ||||||
|  |                     card_removed_notified = false; | ||||||
|  |                 } | ||||||
|  |                 FURI_LOG_D( | ||||||
|  |                     TAG, | ||||||
|  |                     "Try to auth to sector %d with key %04lx%08lx", | ||||||
|  |                     i, | ||||||
|  |                     (uint32_t)(key >> 32), | ||||||
|  |                     (uint32_t)key); | ||||||
|  |                 if(!is_key_a_found) { | ||||||
|  |                     is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); | ||||||
|  |                     if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyA)) { | ||||||
|  |                         mf_classic_set_key_found(data, i, MfClassicKeyA, key); | ||||||
|  |                         nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); | ||||||
|  |                     } | ||||||
|  |                     furi_hal_nfc_sleep(); | ||||||
|  |                 } | ||||||
|  |                 if(!is_key_b_found) { | ||||||
|  |                     is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); | ||||||
|  |                     if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyB)) { | ||||||
|  |                         mf_classic_set_key_found(data, i, MfClassicKeyB, key); | ||||||
|  |                         nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if(is_key_a_found && is_key_b_found) break; | ||||||
|  |                 if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || | ||||||
|  |                      (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) | ||||||
|  |                     break; | ||||||
|  |             } else { | ||||||
|  |                 if(!card_removed_notified) { | ||||||
|  |                     nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); | ||||||
|  |                     card_removed_notified = true; | ||||||
|  |                     card_found_notified = false; | ||||||
|  |                 } | ||||||
|  |                 if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || | ||||||
|  |                      (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || | ||||||
|  |              (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) | ||||||
|  |             break; | ||||||
|  |         mf_classic_read_sector(&tx_rx, data, i); | ||||||
|  |         mf_classic_dict_rewind(dict); | ||||||
|  |     } | ||||||
|  |     mf_classic_dict_free(dict); | ||||||
|  |     if((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || | ||||||
|  |        (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)) { | ||||||
|  |         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|  |     } else { | ||||||
|  |         nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { | ||||||
|  |     FuriHalNfcTxRxContext tx_rx = {}; | ||||||
|  |     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     MfClassicEmulator emulator = { | ||||||
|  |         .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), | ||||||
|  |         .data = nfc_worker->dev_data->mf_classic_data, | ||||||
|  |         .data_changed = false, | ||||||
|  |     }; | ||||||
|  |     NfcaSignal* nfca_signal = nfca_signal_alloc(); | ||||||
|  |     tx_rx.nfca_signal = nfca_signal; | ||||||
|  | 
 | ||||||
|  |     rfal_platform_spi_acquire(); | ||||||
|  | 
 | ||||||
|  |     furi_hal_nfc_listen_start(nfc_data); | ||||||
|  |     while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { | ||||||
|  |         if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { | ||||||
|  |             mf_classic_emulator(&emulator, &tx_rx); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if(emulator.data_changed) { | ||||||
|  |         nfc_worker->dev_data->mf_classic_data = emulator.data; | ||||||
|  |         if(nfc_worker->callback) { | ||||||
|  |             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||||
|  |         } | ||||||
|  |         emulator.data_changed = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     nfca_signal_free(nfca_signal); | ||||||
|  | 
 | ||||||
|  |     rfal_platform_spi_release(); | ||||||
|  | } | ||||||
| @ -10,17 +10,15 @@ typedef enum { | |||||||
|     NfcWorkerStateBroken, |     NfcWorkerStateBroken, | ||||||
|     NfcWorkerStateReady, |     NfcWorkerStateReady, | ||||||
|     // Main worker states
 |     // Main worker states
 | ||||||
|     NfcWorkerStateDetect, |     NfcWorkerStateRead, | ||||||
|     NfcWorkerStateEmulate, |     NfcWorkerStateUidEmulate, | ||||||
|     NfcWorkerStateReadEMVApp, |     NfcWorkerStateMfUltralightEmulate, | ||||||
|     NfcWorkerStateReadEMVData, |     NfcWorkerStateMfClassicEmulate, | ||||||
|  |     NfcWorkerStateMfClassicUserDictAttack, | ||||||
|  |     NfcWorkerStateMfClassicFlipperDictAttack, | ||||||
|  |     // Debug
 | ||||||
|     NfcWorkerStateEmulateApdu, |     NfcWorkerStateEmulateApdu, | ||||||
|     NfcWorkerStateField, |     NfcWorkerStateField, | ||||||
|     NfcWorkerStateReadMifareUltralight, |  | ||||||
|     NfcWorkerStateEmulateMifareUltralight, |  | ||||||
|     NfcWorkerStateReadMifareClassic, |  | ||||||
|     NfcWorkerStateEmulateMifareClassic, |  | ||||||
|     NfcWorkerStateReadMifareDesfire, |  | ||||||
|     // Transition
 |     // Transition
 | ||||||
|     NfcWorkerStateStop, |     NfcWorkerStateStop, | ||||||
| } NfcWorkerState; | } NfcWorkerState; | ||||||
| @ -29,21 +27,33 @@ typedef enum { | |||||||
|     // Reserve first 50 events for application events
 |     // Reserve first 50 events for application events
 | ||||||
|     NfcWorkerEventReserved = 50, |     NfcWorkerEventReserved = 50, | ||||||
| 
 | 
 | ||||||
|  |     // Nfc read events
 | ||||||
|  |     NfcWorkerEventReadUidNfcB, | ||||||
|  |     NfcWorkerEventReadUidNfcV, | ||||||
|  |     NfcWorkerEventReadUidNfcF, | ||||||
|  |     NfcWorkerEventReadUidNfcA, | ||||||
|  |     NfcWorkerEventReadMfUltralight, | ||||||
|  |     NfcWorkerEventReadMfDesfire, | ||||||
|  |     NfcWorkerEventReadMfClassicDone, | ||||||
|  |     NfcWorkerEventReadMfClassicLoadKeyCache, | ||||||
|  |     NfcWorkerEventReadMfClassicDictAttackRequired, | ||||||
|  |     NfcWorkerEventReadBankCard, | ||||||
|  | 
 | ||||||
|     // Nfc worker common events
 |     // Nfc worker common events
 | ||||||
|     NfcWorkerEventSuccess, |     NfcWorkerEventSuccess, | ||||||
|     NfcWorkerEventFail, |     NfcWorkerEventFail, | ||||||
|  |     NfcWorkerEventAborted, | ||||||
|  |     NfcWorkerEventCardDetected, | ||||||
|     NfcWorkerEventNoCardDetected, |     NfcWorkerEventNoCardDetected, | ||||||
|  | 
 | ||||||
|     // Mifare Classic events
 |     // Mifare Classic events
 | ||||||
|     NfcWorkerEventNoDictFound, |     NfcWorkerEventNoDictFound, | ||||||
|     NfcWorkerEventDetectedClassic1k, |  | ||||||
|     NfcWorkerEventDetectedClassic4k, |  | ||||||
|     NfcWorkerEventNewSector, |     NfcWorkerEventNewSector, | ||||||
|     NfcWorkerEventFoundKeyA, |     NfcWorkerEventFoundKeyA, | ||||||
|     NfcWorkerEventFoundKeyB, |     NfcWorkerEventFoundKeyB, | ||||||
|     NfcWorkerEventStartReading, |  | ||||||
| } NfcWorkerEvent; | } NfcWorkerEvent; | ||||||
| 
 | 
 | ||||||
| typedef void (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | ||||||
| 
 | 
 | ||||||
| NfcWorker* nfc_worker_alloc(); | NfcWorker* nfc_worker_alloc(); | ||||||
| 
 | 
 | ||||||
							
								
								
									
										48
									
								
								lib/nfc/nfc_worker_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/nfc/nfc_worker_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_worker.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <lib/toolbox/stream/file_stream.h> | ||||||
|  | 
 | ||||||
|  | #include <lib/nfc/protocols/nfc_util.h> | ||||||
|  | #include <lib/nfc/protocols/emv.h> | ||||||
|  | #include <lib/nfc/protocols/mifare_common.h> | ||||||
|  | #include <lib/nfc/protocols/mifare_ultralight.h> | ||||||
|  | #include <lib/nfc/protocols/mifare_classic.h> | ||||||
|  | #include <lib/nfc/protocols/mifare_desfire.h> | ||||||
|  | #include <lib/nfc/protocols/nfca.h> | ||||||
|  | 
 | ||||||
|  | #include "helpers/mf_classic_dict.h" | ||||||
|  | #include "helpers/nfc_debug_pcap.h" | ||||||
|  | 
 | ||||||
|  | struct NfcWorker { | ||||||
|  |     FuriThread* thread; | ||||||
|  |     Storage* storage; | ||||||
|  |     Stream* dict_stream; | ||||||
|  | 
 | ||||||
|  |     NfcDeviceData* dev_data; | ||||||
|  | 
 | ||||||
|  |     NfcWorkerCallback callback; | ||||||
|  |     void* context; | ||||||
|  | 
 | ||||||
|  |     NfcWorkerState state; | ||||||
|  | 
 | ||||||
|  |     NfcDebugPcapWorker* debug_pcap_worker; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); | ||||||
|  | 
 | ||||||
|  | int32_t nfc_worker_task(void* context); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_read(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_uid(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type); | ||||||
|  | 
 | ||||||
|  | void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); | ||||||
							
								
								
									
										12
									
								
								lib/nfc/parsers/nfc_supported_card.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/nfc/parsers/nfc_supported_card.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | #include "troyka_parser.h" | ||||||
|  | 
 | ||||||
|  | NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { | ||||||
|  |     [NfcSupportedCardTypeTroyka] = { | ||||||
|  |         .protocol = NfcDeviceProtocolMifareClassic, | ||||||
|  |         .verify = troyka_parser_verify, | ||||||
|  |         .read = troyka_parser_read, | ||||||
|  |         .parse = troyka_parser_parse, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
							
								
								
									
										27
									
								
								lib/nfc/parsers/nfc_supported_card.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/nfc/parsers/nfc_supported_card.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi_hal_nfc.h> | ||||||
|  | #include "../nfc_worker.h" | ||||||
|  | 
 | ||||||
|  | #include <m-string.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     NfcSupportedCardTypeTroyka, | ||||||
|  | 
 | ||||||
|  |     NfcSupportedCardTypeEnd, | ||||||
|  | } NfcSupportedCardType; | ||||||
|  | 
 | ||||||
|  | typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | typedef bool (*NfcSupportedCardParse)(NfcWorker* nfc_worker); | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     NfcProtocol protocol; | ||||||
|  |     NfcSupportedCardVerify verify; | ||||||
|  |     NfcSupportedCardRead read; | ||||||
|  |     NfcSupportedCardParse parse; | ||||||
|  | } NfcSupportedCard; | ||||||
|  | 
 | ||||||
|  | extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd]; | ||||||
							
								
								
									
										70
									
								
								lib/nfc/parsers/troyka_parser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								lib/nfc/parsers/troyka_parser.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <nfc_worker_i.h> | ||||||
|  | 
 | ||||||
|  | static const MfClassicAuthContext troyka_keys[] = { | ||||||
|  |     {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, | ||||||
|  |     {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, | ||||||
|  |     {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5}, | ||||||
|  |     {.sector = 5, .key_a = 0xfbc2793d540b, .key_b = 0xd3a297dc2698}, | ||||||
|  |     {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dba}, | ||||||
|  |     {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, | ||||||
|  |     {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, | ||||||
|  |     {.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d}, | ||||||
|  |     {.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b}, | ||||||
|  |     {.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0}, | ||||||
|  |     {.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, | ||||||
|  |     {.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75}, | ||||||
|  |     {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     UNUSED(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     MfClassicAuthContext auth_ctx = { | ||||||
|  |         .key_a = MF_CLASSIC_NO_KEY, | ||||||
|  |         .key_b = MF_CLASSIC_NO_KEY, | ||||||
|  |         .sector = 8, | ||||||
|  |     }; | ||||||
|  |     return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     MfClassicReader reader = {}; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     mf_classic_get_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak, &reader); | ||||||
|  |     for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { | ||||||
|  |         mf_classic_reader_add_sector( | ||||||
|  |             &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool troyka_parser_parse(NfcWorker* nfc_worker) { | ||||||
|  |     MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; | ||||||
|  |     uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; | ||||||
|  |     uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; | ||||||
|  |     temp_ptr = &data->block[8 * 4].value[3]; | ||||||
|  |     uint32_t number = 0; | ||||||
|  |     for(size_t i = 0; i < 4; i++) { | ||||||
|  |         number <<= 8; | ||||||
|  |         number |= temp_ptr[i]; | ||||||
|  |     } | ||||||
|  |     number >>= 4; | ||||||
|  | 
 | ||||||
|  |     string_printf( | ||||||
|  |         nfc_worker->dev_data->parsed_data, | ||||||
|  |         "Troyka Transport card\nNumber: %ld\nBalance: %d rub", | ||||||
|  |         number, | ||||||
|  |         balance); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								lib/nfc/parsers/troyka_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/nfc/parsers/troyka_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool troyka_parser_parse(NfcWorker* nfc_worker); | ||||||
| @ -25,6 +25,16 @@ typedef enum { | |||||||
|     MfClassicActionACWrite, |     MfClassicActionACWrite, | ||||||
| } MfClassicAction; | } MfClassicAction; | ||||||
| 
 | 
 | ||||||
|  | const char* mf_classic_get_type_str(MfClassicType type) { | ||||||
|  |     if(type == MfClassicType1k) { | ||||||
|  |         return "MIFARE Classic 1K"; | ||||||
|  |     } else if(type == MfClassicType4k) { | ||||||
|  |         return "MIFARE Classic 4K"; | ||||||
|  |     } else { | ||||||
|  |         return "Unknown"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { | static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { | ||||||
|     furi_assert(sector < 40); |     furi_assert(sector < 40); | ||||||
|     if(sector < 32) { |     if(sector < 32) { | ||||||
| @ -34,7 +44,16 @@ static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint8_t mf_classic_get_sector_by_block(uint8_t block) { | uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) { | ||||||
|  |     furi_assert(sector < 40); | ||||||
|  |     if(sector < 32) { | ||||||
|  |         return sector * 4 + 3; | ||||||
|  |     } else { | ||||||
|  |         return 32 * 4 + (sector - 32) * 16 + 15; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t mf_classic_get_sector_by_block(uint8_t block) { | ||||||
|     if(block < 128) { |     if(block < 128) { | ||||||
|         return (block | 0x03) / 4; |         return (block | 0x03) / 4; | ||||||
|     } else { |     } else { | ||||||
| @ -47,7 +66,7 @@ static uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { | |||||||
|     return sector < 32 ? 4 : 16; |     return sector < 32 ? 4 : 16; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint8_t mf_classic_get_sector_trailer(uint8_t block) { | uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { | ||||||
|     if(block < 128) { |     if(block < 128) { | ||||||
|         return block | 0x03; |         return block | 0x03; | ||||||
|     } else { |     } else { | ||||||
| @ -55,15 +74,21 @@ static uint8_t mf_classic_get_sector_trailer(uint8_t block) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool mf_classic_is_sector_trailer(uint8_t block) { | bool mf_classic_is_sector_trailer(uint8_t block) { | ||||||
|     return block == mf_classic_get_sector_trailer(block); |     return block == mf_classic_get_sector_trailer_num_by_block(block); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader) { | MfClassicSectorTrailer* | ||||||
|     furi_assert(reader); |     mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector) { | ||||||
|     if(reader->type == MfClassicType1k) { |     furi_assert(data); | ||||||
|  |     uint8_t sec_tr_block_num = mf_classic_get_sector_trailer_block_num_by_sector(sector); | ||||||
|  |     return (MfClassicSectorTrailer*)data->block[sec_tr_block_num].value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { | ||||||
|  |     if(type == MfClassicType1k) { | ||||||
|         return MF_CLASSIC_1K_TOTAL_SECTORS_NUM; |         return MF_CLASSIC_1K_TOTAL_SECTORS_NUM; | ||||||
|     } else if(reader->type == MfClassicType4k) { |     } else if(type == MfClassicType4k) { | ||||||
|         return MF_CLASSIC_4K_TOTAL_SECTORS_NUM; |         return MF_CLASSIC_4K_TOTAL_SECTORS_NUM; | ||||||
|     } else { |     } else { | ||||||
|         return 0; |         return 0; | ||||||
| @ -80,6 +105,104 @@ static uint16_t mf_classic_get_total_block_num(MfClassicType type) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num) { | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) { | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     if(mf_classic_is_sector_trailer(block_num)) { | ||||||
|  |         memcpy(&data->block[block_num].value[6], &block_data->value[6], 4); | ||||||
|  |     } else { | ||||||
|  |         memcpy(data->block[block_num].value, block_data->value, MF_CLASSIC_BLOCK_SIZE); | ||||||
|  |     } | ||||||
|  |     FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     bool key_found = false; | ||||||
|  |     if(key_type == MfClassicKeyA) { | ||||||
|  |         key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1); | ||||||
|  |     } else if(key_type == MfClassicKeyB) { | ||||||
|  |         key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return key_found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_classic_set_key_found( | ||||||
|  |     MfClassicData* data, | ||||||
|  |     uint8_t sector_num, | ||||||
|  |     MfClassicKey key_type, | ||||||
|  |     uint64_t key) { | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     uint8_t key_arr[6] = {}; | ||||||
|  |     MfClassicSectorTrailer* sec_trailer = | ||||||
|  |         mf_classic_get_sector_trailer_by_sector(data, sector_num); | ||||||
|  |     nfc_util_num2bytes(key, 6, key_arr); | ||||||
|  |     if(key_type == MfClassicKeyA) { | ||||||
|  |         memcpy(sec_trailer->key_a, key_arr, sizeof(sec_trailer->key_a)); | ||||||
|  |         FURI_BIT_SET(data->key_a_mask, sector_num); | ||||||
|  |     } else if(key_type == MfClassicKeyB) { | ||||||
|  |         memcpy(sec_trailer->key_b, key_arr, sizeof(sec_trailer->key_b)); | ||||||
|  |         FURI_BIT_SET(data->key_b_mask, sector_num); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) { | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     bool sector_read = false; | ||||||
|  |     do { | ||||||
|  |         if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) break; | ||||||
|  |         if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) break; | ||||||
|  |         uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num); | ||||||
|  |         uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); | ||||||
|  |         uint8_t block_read = true; | ||||||
|  |         for(size_t i = start_block; i < start_block + total_blocks; i++) { | ||||||
|  |             block_read = mf_classic_is_block_read(data, i); | ||||||
|  |             if(!block_read) break; | ||||||
|  |         } | ||||||
|  |         sector_read = block_read; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return sector_read; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mf_classic_get_read_sectors_and_keys( | ||||||
|  |     MfClassicData* data, | ||||||
|  |     uint8_t* sectors_read, | ||||||
|  |     uint8_t* keys_found) { | ||||||
|  |     furi_assert(data); | ||||||
|  |     *sectors_read = 0; | ||||||
|  |     *keys_found = 0; | ||||||
|  |     uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); | ||||||
|  |     for(size_t i = 0; i < sectors_total; i++) { | ||||||
|  |         if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { | ||||||
|  |             *keys_found += 1; | ||||||
|  |         } | ||||||
|  |         if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { | ||||||
|  |             *keys_found += 1; | ||||||
|  |         } | ||||||
|  |         uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); | ||||||
|  |         uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); | ||||||
|  |         bool blocks_read = true; | ||||||
|  |         for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) { | ||||||
|  |             blocks_read = mf_classic_is_block_read(data, i); | ||||||
|  |             if(!blocks_read) break; | ||||||
|  |         } | ||||||
|  |         if(blocks_read) { | ||||||
|  |             *sectors_read += 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool mf_classic_is_allowed_access_sector_trailer( | static bool mf_classic_is_allowed_access_sector_trailer( | ||||||
|     MfClassicEmulator* emulator, |     MfClassicEmulator* emulator, | ||||||
|     uint8_t block_num, |     uint8_t block_num, | ||||||
| @ -126,7 +249,8 @@ static bool mf_classic_is_allowed_access_data_block( | |||||||
|     uint8_t block_num, |     uint8_t block_num, | ||||||
|     MfClassicKey key, |     MfClassicKey key, | ||||||
|     MfClassicAction action) { |     MfClassicAction action) { | ||||||
|     uint8_t* sector_trailer = emulator->data.block[mf_classic_get_sector_trailer(block_num)].value; |     uint8_t* sector_trailer = | ||||||
|  |         emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; | ||||||
| 
 | 
 | ||||||
|     uint8_t sector_block; |     uint8_t sector_block; | ||||||
|     if(block_num <= 128) { |     if(block_num <= 128) { | ||||||
| @ -207,15 +331,18 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool mf_classic_get_type( | MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { | ||||||
|     uint8_t* uid, |     UNUSED(ATQA1); | ||||||
|     uint8_t uid_len, |     if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { | ||||||
|     uint8_t ATQA0, |         return MfClassicType1k; | ||||||
|     uint8_t ATQA1, |     } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { | ||||||
|     uint8_t SAK, |         return MfClassicType4k; | ||||||
|     MfClassicReader* reader) { |     } | ||||||
|  |     return MfClassicType1k; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool mf_classic_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK, MfClassicReader* reader) { | ||||||
|     UNUSED(ATQA1); |     UNUSED(ATQA1); | ||||||
|     furi_assert(uid); |  | ||||||
|     furi_assert(reader); |     furi_assert(reader); | ||||||
|     memset(reader, 0, sizeof(MfClassicReader)); |     memset(reader, 0, sizeof(MfClassicReader)); | ||||||
| 
 | 
 | ||||||
| @ -226,14 +353,6 @@ bool mf_classic_get_type( | |||||||
|     } else { |     } else { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     uint8_t* cuid_start = uid; |  | ||||||
|     if(uid_len == 7) { |  | ||||||
|         cuid_start = &uid[3]; |  | ||||||
|     } |  | ||||||
|     reader->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | |  | ||||||
|                    (cuid_start[3]); |  | ||||||
| 
 |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -246,7 +365,7 @@ void mf_classic_reader_add_sector( | |||||||
|     furi_assert(sector < MF_CLASSIC_SECTORS_MAX); |     furi_assert(sector < MF_CLASSIC_SECTORS_MAX); | ||||||
|     furi_assert((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)); |     furi_assert((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)); | ||||||
| 
 | 
 | ||||||
|     if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX - 1) { |     if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX) { | ||||||
|         reader->sector_reader[reader->sectors_to_read].key_a = key_a; |         reader->sector_reader[reader->sectors_to_read].key_a = key_a; | ||||||
|         reader->sector_reader[reader->sectors_to_read].key_b = key_b; |         reader->sector_reader[reader->sectors_to_read].key_b = key_b; | ||||||
|         reader->sector_reader[reader->sectors_to_read].sector_num = sector; |         reader->sector_reader[reader->sectors_to_read].sector_num = sector; | ||||||
| @ -254,9 +373,8 @@ void mf_classic_reader_add_sector( | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, uint8_t sector) { | void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { | ||||||
|     furi_assert(auth_ctx); |     furi_assert(auth_ctx); | ||||||
|     auth_ctx->cuid = cuid; |  | ||||||
|     auth_ctx->sector = sector; |     auth_ctx->sector = sector; | ||||||
|     auth_ctx->key_a = MF_CLASSIC_NO_KEY; |     auth_ctx->key_a = MF_CLASSIC_NO_KEY; | ||||||
|     auth_ctx->key_b = MF_CLASSIC_NO_KEY; |     auth_ctx->key_b = MF_CLASSIC_NO_KEY; | ||||||
| @ -264,17 +382,18 @@ void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, | |||||||
| 
 | 
 | ||||||
| static bool mf_classic_auth( | static bool mf_classic_auth( | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|     uint32_t cuid, |  | ||||||
|     uint32_t block, |     uint32_t block, | ||||||
|     uint64_t key, |     uint64_t key, | ||||||
|     MfClassicKey key_type, |     MfClassicKey key_type, | ||||||
|     Crypto1* crypto) { |     Crypto1* crypto) { | ||||||
|     bool auth_success = false; |     bool auth_success = false; | ||||||
|  |     uint32_t cuid = 0; | ||||||
|     memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); |     memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); | ||||||
|     memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); |     memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); | ||||||
|     tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; |     tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|  |         if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; | ||||||
|         if(key_type == MfClassicKeyA) { |         if(key_type == MfClassicKeyA) { | ||||||
|             tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_A_CMD; |             tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_A_CMD; | ||||||
|         } else { |         } else { | ||||||
| @ -315,6 +434,19 @@ static bool mf_classic_auth( | |||||||
|     return auth_success; |     return auth_success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool mf_classic_authenticate( | ||||||
|  |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|  |     uint8_t block_num, | ||||||
|  |     uint64_t key, | ||||||
|  |     MfClassicKey key_type) { | ||||||
|  |     furi_assert(tx_rx); | ||||||
|  | 
 | ||||||
|  |     Crypto1 crypto = {}; | ||||||
|  |     bool key_found = mf_classic_auth(tx_rx, block_num, key, key_type, &crypto); | ||||||
|  |     furi_hal_nfc_sleep(); | ||||||
|  |     return key_found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool mf_classic_auth_attempt( | bool mf_classic_auth_attempt( | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|     MfClassicAuthContext* auth_ctx, |     MfClassicAuthContext* auth_ctx, | ||||||
| @ -330,7 +462,6 @@ bool mf_classic_auth_attempt( | |||||||
|         // Try AUTH with key A
 |         // Try AUTH with key A
 | ||||||
|         if(mf_classic_auth( |         if(mf_classic_auth( | ||||||
|                tx_rx, |                tx_rx, | ||||||
|                auth_ctx->cuid, |  | ||||||
|                mf_classic_get_first_block_num_of_sector(auth_ctx->sector), |                mf_classic_get_first_block_num_of_sector(auth_ctx->sector), | ||||||
|                key, |                key, | ||||||
|                MfClassicKeyA, |                MfClassicKeyA, | ||||||
| @ -342,14 +473,12 @@ bool mf_classic_auth_attempt( | |||||||
| 
 | 
 | ||||||
|     if(need_halt) { |     if(need_halt) { | ||||||
|         furi_hal_nfc_sleep(); |         furi_hal_nfc_sleep(); | ||||||
|         furi_hal_nfc_activate_nfca(300, &auth_ctx->cuid); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(auth_ctx->key_b == MF_CLASSIC_NO_KEY) { |     if(auth_ctx->key_b == MF_CLASSIC_NO_KEY) { | ||||||
|         // Try AUTH with key B
 |         // Try AUTH with key B
 | ||||||
|         if(mf_classic_auth( |         if(mf_classic_auth( | ||||||
|                tx_rx, |                tx_rx, | ||||||
|                auth_ctx->cuid, |  | ||||||
|                mf_classic_get_first_block_num_of_sector(auth_ctx->sector), |                mf_classic_get_first_block_num_of_sector(auth_ctx->sector), | ||||||
|                key, |                key, | ||||||
|                MfClassicKeyB, |                MfClassicKeyB, | ||||||
| @ -410,7 +539,60 @@ bool mf_classic_read_block( | |||||||
|     return read_block_success; |     return read_block_success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool mf_classic_read_sector( | void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num) { | ||||||
|  |     furi_assert(tx_rx); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     furi_hal_nfc_sleep(); | ||||||
|  |     bool key_a_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyA); | ||||||
|  |     bool key_b_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyB); | ||||||
|  |     uint8_t start_block = mf_classic_get_first_block_num_of_sector(sec_num); | ||||||
|  |     uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); | ||||||
|  |     MfClassicBlock block_tmp = {}; | ||||||
|  |     uint64_t key = 0; | ||||||
|  |     MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num); | ||||||
|  |     Crypto1 crypto = {}; | ||||||
|  | 
 | ||||||
|  |     uint8_t blocks_read = 0; | ||||||
|  |     do { | ||||||
|  |         if(!key_a_found) break; | ||||||
|  |         FURI_LOG_D(TAG, "Try to read blocks with key A"); | ||||||
|  |         key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a)); | ||||||
|  |         if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto)) break; | ||||||
|  |         for(size_t i = start_block; i < start_block + total_blocks; i++) { | ||||||
|  |             if(!mf_classic_is_block_read(data, i)) { | ||||||
|  |                 if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { | ||||||
|  |                     mf_classic_set_block_read(data, i, &block_tmp); | ||||||
|  |                     blocks_read++; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 blocks_read++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks); | ||||||
|  |     } while(false); | ||||||
|  |     do { | ||||||
|  |         if(blocks_read == total_blocks) break; | ||||||
|  |         if(!key_b_found) break; | ||||||
|  |         FURI_LOG_D(TAG, "Try to read blocks with key B"); | ||||||
|  |         key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); | ||||||
|  |         furi_hal_nfc_sleep(); | ||||||
|  |         if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto)) break; | ||||||
|  |         for(size_t i = start_block; i < start_block + total_blocks; i++) { | ||||||
|  |             if(!mf_classic_is_block_read(data, i)) { | ||||||
|  |                 if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { | ||||||
|  |                     mf_classic_set_block_read(data, i, &block_tmp); | ||||||
|  |                     blocks_read++; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 blocks_read++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks); | ||||||
|  |     } while(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool mf_classic_read_sector_with_reader( | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|     Crypto1* crypto, |     Crypto1* crypto, | ||||||
|     MfClassicSectorReader* sector_reader, |     MfClassicSectorReader* sector_reader, | ||||||
| @ -419,7 +601,6 @@ bool mf_classic_read_sector( | |||||||
|     furi_assert(sector_reader); |     furi_assert(sector_reader); | ||||||
|     furi_assert(sector); |     furi_assert(sector); | ||||||
| 
 | 
 | ||||||
|     uint32_t cuid = 0; |  | ||||||
|     uint64_t key; |     uint64_t key; | ||||||
|     MfClassicKey key_type; |     MfClassicKey key_type; | ||||||
|     uint8_t first_block; |     uint8_t first_block; | ||||||
| @ -428,7 +609,6 @@ bool mf_classic_read_sector( | |||||||
|     furi_hal_nfc_sleep(); |     furi_hal_nfc_sleep(); | ||||||
|     do { |     do { | ||||||
|         // Activate card
 |         // Activate card
 | ||||||
|         if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; |  | ||||||
|         first_block = mf_classic_get_first_block_num_of_sector(sector_reader->sector_num); |         first_block = mf_classic_get_first_block_num_of_sector(sector_reader->sector_num); | ||||||
|         if(sector_reader->key_a != MF_CLASSIC_NO_KEY) { |         if(sector_reader->key_a != MF_CLASSIC_NO_KEY) { | ||||||
|             key = sector_reader->key_a; |             key = sector_reader->key_a; | ||||||
| @ -441,7 +621,7 @@ bool mf_classic_read_sector( | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Auth to first block in sector
 |         // Auth to first block in sector
 | ||||||
|         if(!mf_classic_auth(tx_rx, cuid, first_block, key, key_type, crypto)) break; |         if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break; | ||||||
|         sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num); |         sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num); | ||||||
| 
 | 
 | ||||||
|         // Read blocks
 |         // Read blocks
 | ||||||
| @ -478,18 +658,26 @@ uint8_t mf_classic_read_card( | |||||||
|     data->key_b_mask = 0; |     data->key_b_mask = 0; | ||||||
|     MfClassicSector temp_sector = {}; |     MfClassicSector temp_sector = {}; | ||||||
|     for(uint8_t i = 0; i < reader->sectors_to_read; i++) { |     for(uint8_t i = 0; i < reader->sectors_to_read; i++) { | ||||||
|         if(mf_classic_read_sector( |         if(mf_classic_read_sector_with_reader( | ||||||
|                tx_rx, &reader->crypto, &reader->sector_reader[i], &temp_sector)) { |                tx_rx, &reader->crypto, &reader->sector_reader[i], &temp_sector)) { | ||||||
|             uint8_t first_block = |             uint8_t first_block = | ||||||
|                 mf_classic_get_first_block_num_of_sector(reader->sector_reader[i].sector_num); |                 mf_classic_get_first_block_num_of_sector(reader->sector_reader[i].sector_num); | ||||||
|             for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { |             for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { | ||||||
|                 data->block[first_block + j] = temp_sector.block[j]; |                 mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); | ||||||
|             } |             } | ||||||
|             if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) { |             if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) { | ||||||
|                 data->key_a_mask |= 1 << reader->sector_reader[i].sector_num; |                 mf_classic_set_key_found( | ||||||
|  |                     data, | ||||||
|  |                     reader->sector_reader[i].sector_num, | ||||||
|  |                     MfClassicKeyA, | ||||||
|  |                     reader->sector_reader[i].key_a); | ||||||
|             } |             } | ||||||
|             if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) { |             if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) { | ||||||
|                 data->key_b_mask |= 1 << reader->sector_reader[i].sector_num; |                 mf_classic_set_key_found( | ||||||
|  |                     data, | ||||||
|  |                     reader->sector_reader[i].sector_num, | ||||||
|  |                     MfClassicKeyB, | ||||||
|  |                     reader->sector_reader[i].key_b); | ||||||
|             } |             } | ||||||
|             sectors_read++; |             sectors_read++; | ||||||
|         } |         } | ||||||
| @ -498,6 +686,46 @@ uint8_t mf_classic_read_card( | |||||||
|     return sectors_read; |     return sectors_read; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data) { | ||||||
|  |     furi_assert(tx_rx); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     uint8_t sectors_read = 0; | ||||||
|  |     Crypto1 crypto = {}; | ||||||
|  |     uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type); | ||||||
|  |     uint64_t key_a = 0; | ||||||
|  |     uint64_t key_b = 0; | ||||||
|  |     MfClassicSectorReader sec_reader = {}; | ||||||
|  |     MfClassicSector temp_sector = {}; | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < total_sectors; i++) { | ||||||
|  |         MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); | ||||||
|  |         // Load key A
 | ||||||
|  |         if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { | ||||||
|  |             sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6); | ||||||
|  |         } else { | ||||||
|  |             sec_reader.key_a = MF_CLASSIC_NO_KEY; | ||||||
|  |         } | ||||||
|  |         // Load key B
 | ||||||
|  |         if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { | ||||||
|  |             sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6); | ||||||
|  |         } else { | ||||||
|  |             sec_reader.key_b = MF_CLASSIC_NO_KEY; | ||||||
|  |         } | ||||||
|  |         if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) { | ||||||
|  |             sec_reader.sector_num = i; | ||||||
|  |             if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) { | ||||||
|  |                 uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); | ||||||
|  |                 for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { | ||||||
|  |                     mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); | ||||||
|  |                 } | ||||||
|  |                 sectors_read++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return sectors_read; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void mf_crypto1_decrypt( | void mf_crypto1_decrypt( | ||||||
|     Crypto1* crypto, |     Crypto1* crypto, | ||||||
|     uint8_t* encrypted_data, |     uint8_t* encrypted_data, | ||||||
| @ -573,7 +801,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ | |||||||
|         } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { |         } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { | ||||||
|             uint8_t block = plain_data[1]; |             uint8_t block = plain_data[1]; | ||||||
|             uint64_t key = 0; |             uint64_t key = 0; | ||||||
|             uint8_t sector_trailer_block = mf_classic_get_sector_trailer(block); |             uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); | ||||||
|             MfClassicSectorTrailer* sector_trailer = |             MfClassicSectorTrailer* sector_trailer = | ||||||
|                 (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; |                 (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; | ||||||
|             if(plain_data[0] == 0x60) { |             if(plain_data[0] == 0x60) { | ||||||
| @ -635,21 +863,6 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ | |||||||
|                 nr, |                 nr, | ||||||
|                 ar); |                 ar); | ||||||
| 
 | 
 | ||||||
|             // Check if we store valid key
 |  | ||||||
|             if(access_key == MfClassicKeyA) { |  | ||||||
|                 if(FURI_BIT(emulator->data.key_a_mask, mf_classic_get_sector_by_block(block)) == |  | ||||||
|                    0) { |  | ||||||
|                     FURI_LOG_D(TAG, "Unsupported sector key A for block %d", sector_trailer_block); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } else if(access_key == MfClassicKeyB) { |  | ||||||
|                 if(FURI_BIT(emulator->data.key_b_mask, mf_classic_get_sector_by_block(block)) == |  | ||||||
|                    0) { |  | ||||||
|                     FURI_LOG_D(TAG, "Unsupported sector key B for block %d", sector_trailer_block); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             crypto1_word(&emulator->crypto, nr, 1); |             crypto1_word(&emulator->crypto, nr, 1); | ||||||
|             uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); |             uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); | ||||||
|             if(cardRr != prng_successor(nonce, 64)) { |             if(cardRr != prng_successor(nonce, 64)) { | ||||||
| @ -14,6 +14,8 @@ | |||||||
| 
 | 
 | ||||||
| #define MF_CLASSIC_NO_KEY (0xFFFFFFFFFFFFFFFF) | #define MF_CLASSIC_NO_KEY (0xFFFFFFFFFFFFFFFF) | ||||||
| #define MF_CLASSIC_MAX_DATA_SIZE (16) | #define MF_CLASSIC_MAX_DATA_SIZE (16) | ||||||
|  | #define MF_CLASSIC_KEY_SIZE (6) | ||||||
|  | #define MF_CLASSIC_ACCESS_BYTES_SIZE (4) | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     MfClassicType1k, |     MfClassicType1k, | ||||||
| @ -30,9 +32,9 @@ typedef struct { | |||||||
| } MfClassicBlock; | } MfClassicBlock; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t key_a[6]; |     uint8_t key_a[MF_CLASSIC_KEY_SIZE]; | ||||||
|     uint8_t access_bits[4]; |     uint8_t access_bits[MF_CLASSIC_ACCESS_BYTES_SIZE]; | ||||||
|     uint8_t key_b[6]; |     uint8_t key_b[MF_CLASSIC_KEY_SIZE]; | ||||||
| } MfClassicSectorTrailer; | } MfClassicSectorTrailer; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -42,13 +44,13 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     MfClassicType type; |     MfClassicType type; | ||||||
|  |     uint32_t block_read_mask[MF_CLASSIC_TOTAL_BLOCKS_MAX / 32]; | ||||||
|     uint64_t key_a_mask; |     uint64_t key_a_mask; | ||||||
|     uint64_t key_b_mask; |     uint64_t key_b_mask; | ||||||
|     MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; |     MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; | ||||||
| } MfClassicData; | } MfClassicData; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint32_t cuid; |  | ||||||
|     uint8_t sector; |     uint8_t sector; | ||||||
|     uint64_t key_a; |     uint64_t key_a; | ||||||
|     uint64_t key_b; |     uint64_t key_b; | ||||||
| @ -62,9 +64,8 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     MfClassicType type; |     MfClassicType type; | ||||||
|     uint32_t cuid; |  | ||||||
|     uint8_t sectors_to_read; |  | ||||||
|     Crypto1 crypto; |     Crypto1 crypto; | ||||||
|  |     uint8_t sectors_to_read; | ||||||
|     MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX]; |     MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX]; | ||||||
| } MfClassicReader; | } MfClassicReader; | ||||||
| 
 | 
 | ||||||
| @ -75,19 +76,51 @@ typedef struct { | |||||||
|     bool data_changed; |     bool data_changed; | ||||||
| } MfClassicEmulator; | } MfClassicEmulator; | ||||||
| 
 | 
 | ||||||
|  | const char* mf_classic_get_type_str(MfClassicType type); | ||||||
|  | 
 | ||||||
| bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); | bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); | ||||||
| 
 | 
 | ||||||
| bool mf_classic_get_type( | MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK); | ||||||
|     uint8_t* uid, |  | ||||||
|     uint8_t uid_len, |  | ||||||
|     uint8_t ATQA0, |  | ||||||
|     uint8_t ATQA1, |  | ||||||
|     uint8_t SAK, |  | ||||||
|     MfClassicReader* reader); |  | ||||||
| 
 | 
 | ||||||
| uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader); | bool mf_classic_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK, MfClassicReader* reader); | ||||||
| 
 | 
 | ||||||
| void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, uint8_t sector); | uint8_t mf_classic_get_total_sectors_num(MfClassicType type); | ||||||
|  | 
 | ||||||
|  | uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_is_sector_trailer(uint8_t block); | ||||||
|  | 
 | ||||||
|  | uint8_t mf_classic_get_sector_by_block(uint8_t block); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); | ||||||
|  | 
 | ||||||
|  | void mf_classic_set_key_found( | ||||||
|  |     MfClassicData* data, | ||||||
|  |     uint8_t sector_num, | ||||||
|  |     MfClassicKey key_type, | ||||||
|  |     uint64_t key); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); | ||||||
|  | 
 | ||||||
|  | void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); | ||||||
|  | 
 | ||||||
|  | void mf_classic_get_read_sectors_and_keys( | ||||||
|  |     MfClassicData* data, | ||||||
|  |     uint8_t* sectors_read, | ||||||
|  |     uint8_t* keys_found); | ||||||
|  | 
 | ||||||
|  | MfClassicSectorTrailer* | ||||||
|  |     mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector); | ||||||
|  | 
 | ||||||
|  | void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector); | ||||||
|  | 
 | ||||||
|  | bool mf_classic_authenticate( | ||||||
|  |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|  |     uint8_t block_num, | ||||||
|  |     uint64_t key, | ||||||
|  |     MfClassicKey key_type); | ||||||
| 
 | 
 | ||||||
| bool mf_classic_auth_attempt( | bool mf_classic_auth_attempt( | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |     FuriHalNfcTxRxContext* tx_rx, | ||||||
| @ -100,15 +133,13 @@ void mf_classic_reader_add_sector( | |||||||
|     uint64_t key_a, |     uint64_t key_a, | ||||||
|     uint64_t key_b); |     uint64_t key_b); | ||||||
| 
 | 
 | ||||||
| bool mf_classic_read_sector( | void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |  | ||||||
|     Crypto1* crypto, |  | ||||||
|     MfClassicSectorReader* sector_reader, |  | ||||||
|     MfClassicSector* sector); |  | ||||||
| 
 | 
 | ||||||
| uint8_t mf_classic_read_card( | uint8_t mf_classic_read_card( | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |     FuriHalNfcTxRxContext* tx_rx, | ||||||
|     MfClassicReader* reader, |     MfClassicReader* reader, | ||||||
|     MfClassicData* data); |     MfClassicData* data); | ||||||
| 
 | 
 | ||||||
|  | uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); | ||||||
|  | 
 | ||||||
| bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); | bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); | ||||||
| @ -2,6 +2,8 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal_nfc.h> | #include <furi_hal_nfc.h> | ||||||
| 
 | 
 | ||||||
|  | #define TAG "MifareDESFire" | ||||||
|  | 
 | ||||||
| void mf_df_clear(MifareDesfireData* data) { | void mf_df_clear(MifareDesfireData* data) { | ||||||
|     free(data->free_memory); |     free(data->free_memory); | ||||||
|     if(data->master_key_settings) { |     if(data->master_key_settings) { | ||||||
| @ -449,3 +451,173 @@ bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFil | |||||||
|     memcpy(out->contents, buf, len); |     memcpy(out->contents, buf, len); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data) { | ||||||
|  |     furi_assert(tx_rx); | ||||||
|  |     furi_assert(data); | ||||||
|  | 
 | ||||||
|  |     bool card_read = false; | ||||||
|  |     do { | ||||||
|  |         // Get version
 | ||||||
|  |         tx_rx->tx_bits = 8 * mf_df_prepare_get_version(tx_rx->tx_data); | ||||||
|  |         if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |             FURI_LOG_W(TAG, "Bad exchange getting version"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!mf_df_parse_get_version_response(tx_rx->rx_data, tx_rx->rx_bits / 8, &data->version)) { | ||||||
|  |             FURI_LOG_W(TAG, "Bad DESFire GET_VERSION responce"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Get free memory
 | ||||||
|  |         tx_rx->tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx->tx_data); | ||||||
|  |         if(furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); | ||||||
|  |             if(!mf_df_parse_get_free_memory_response( | ||||||
|  |                    tx_rx->rx_data, tx_rx->rx_bits / 8, data->free_memory)) { | ||||||
|  |                 FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); | ||||||
|  |                 free(data->free_memory); | ||||||
|  |                 data->free_memory = NULL; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Get key settings
 | ||||||
|  |         tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data); | ||||||
|  |         if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |             FURI_LOG_D(TAG, "Bad exchange getting key settings"); | ||||||
|  |         } else { | ||||||
|  |             data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||||
|  |             if(!mf_df_parse_get_key_settings_response( | ||||||
|  |                    tx_rx->rx_data, tx_rx->rx_bits / 8, data->master_key_settings)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||||
|  |                 free(data->master_key_settings); | ||||||
|  |                 data->master_key_settings = NULL; | ||||||
|  |             } else { | ||||||
|  |                 MifareDesfireKeyVersion** key_version_head = | ||||||
|  |                     &data->master_key_settings->key_version_head; | ||||||
|  |                 for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { | ||||||
|  |                     tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id); | ||||||
|  |                     if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |                         FURI_LOG_W(TAG, "Bad exchange getting key version"); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                     key_version->id = key_id; | ||||||
|  |                     if(!mf_df_parse_get_key_version_response( | ||||||
|  |                            tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) { | ||||||
|  |                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); | ||||||
|  |                         free(key_version); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     *key_version_head = key_version; | ||||||
|  |                     key_version_head = &key_version->next; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Get application IDs
 | ||||||
|  |         tx_rx->tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx->tx_data); | ||||||
|  |         if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |             FURI_LOG_W(TAG, "Bad exchange getting application IDs"); | ||||||
|  |             break; | ||||||
|  |         } else { | ||||||
|  |             if(!mf_df_parse_get_application_ids_response( | ||||||
|  |                    tx_rx->rx_data, tx_rx->rx_bits / 8, &data->app_head)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  |             tx_rx->tx_bits = 8 * mf_df_prepare_select_application(tx_rx->tx_data, app->id); | ||||||
|  |             if(!furi_hal_nfc_tx_rx_full(tx_rx) || | ||||||
|  |                !mf_df_parse_select_application_response(tx_rx->rx_data, tx_rx->rx_bits / 8)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad exchange selecting application"); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data); | ||||||
|  |             if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad exchange getting key settings"); | ||||||
|  |             } else { | ||||||
|  |                 app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); | ||||||
|  |                 memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); | ||||||
|  |                 if(!mf_df_parse_get_key_settings_response( | ||||||
|  |                        tx_rx->rx_data, tx_rx->rx_bits / 8, app->key_settings)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||||
|  |                     free(app->key_settings); | ||||||
|  |                     app->key_settings = NULL; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; | ||||||
|  |                 for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { | ||||||
|  |                     tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id); | ||||||
|  |                     if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |                         FURI_LOG_W(TAG, "Bad exchange getting key version"); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                     memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); | ||||||
|  |                     key_version->id = key_id; | ||||||
|  |                     if(!mf_df_parse_get_key_version_response( | ||||||
|  |                            tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) { | ||||||
|  |                         FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); | ||||||
|  |                         free(key_version); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     *key_version_head = key_version; | ||||||
|  |                     key_version_head = &key_version->next; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             tx_rx->tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx->tx_data); | ||||||
|  |             if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |                 FURI_LOG_W(TAG, "Bad exchange getting file IDs"); | ||||||
|  |             } else { | ||||||
|  |                 if(!mf_df_parse_get_file_ids_response( | ||||||
|  |                        tx_rx->rx_data, tx_rx->rx_bits / 8, &app->file_head)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||||
|  |                 tx_rx->tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx->tx_data, file->id); | ||||||
|  |                 if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad exchange getting file settings"); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if(!mf_df_parse_get_file_settings_response( | ||||||
|  |                        tx_rx->rx_data, tx_rx->rx_bits / 8, file)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 switch(file->type) { | ||||||
|  |                 case MifareDesfireFileTypeStandard: | ||||||
|  |                 case MifareDesfireFileTypeBackup: | ||||||
|  |                     tx_rx->tx_bits = 8 * mf_df_prepare_read_data(tx_rx->tx_data, file->id, 0, 0); | ||||||
|  |                     break; | ||||||
|  |                 case MifareDesfireFileTypeValue: | ||||||
|  |                     tx_rx->tx_bits = 8 * mf_df_prepare_get_value(tx_rx->tx_data, file->id); | ||||||
|  |                     break; | ||||||
|  |                 case MifareDesfireFileTypeLinearRecord: | ||||||
|  |                 case MifareDesfireFileTypeCyclicRecord: | ||||||
|  |                     tx_rx->tx_bits = | ||||||
|  |                         8 * mf_df_prepare_read_records(tx_rx->tx_data, file->id, 0, 0); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 if(!furi_hal_nfc_tx_rx_full(tx_rx)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if(!mf_df_parse_read_data_response(tx_rx->rx_data, tx_rx->rx_bits / 8, file)) { | ||||||
|  |                     FURI_LOG_W(TAG, "Bad response reading file %d", file->id); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         card_read = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return card_read; | ||||||
|  | } | ||||||
| @ -4,6 +4,8 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
|  | #include <furi_hal_nfc.h> | ||||||
|  | 
 | ||||||
| #define MF_DF_GET_VERSION (0x60) | #define MF_DF_GET_VERSION (0x60) | ||||||
| #define MF_DF_GET_FREE_MEMORY (0x6E) | #define MF_DF_GET_FREE_MEMORY (0x6E) | ||||||
| #define MF_DF_GET_KEY_SETTINGS (0x45) | #define MF_DF_GET_KEY_SETTINGS (0x45) | ||||||
| @ -163,3 +165,5 @@ uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset | |||||||
| uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id); | uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id); | ||||||
| uint16_t mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); | uint16_t mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); | ||||||
| bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); | bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); | ||||||
|  | 
 | ||||||
|  | bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data); | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich