diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c index c62d4816f..12c196d11 100644 --- a/applications/main/nfc/plugins/supported_cards/microel.c +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -1,228 +1,228 @@ -#include "nfc_supported_card_plugin.h" -#include -#include -#include -#include -#include - -#define TAG "Microel" -#define KEY_LENGTH 6 -#define UID_LENGTH 4 - -typedef struct { - uint64_t a; - uint64_t b; -} MfClassicKeyPair; - -static MfClassicKeyPair microel_1k_keys[] = { - {.a = 0x000000000000, .b = 0x000000000000}, // 000 - {.a = 0x000000000000, .b = 0x000000000000}, // 001 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 002 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 003 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 004 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 005 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 006 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 007 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 008 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 009 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 010 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 011 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 012 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 013 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 014 - {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 015 -}; - -const uint8_t verify_sector = 1; - -void calculateSumHex(const uint8_t* uid, size_t uidSize, uint8_t sumHex[]) { - const uint8_t xorKey[] = {0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9}; - int sum = 0; - - for(size_t i = 0; i < uidSize; i++) { - sum += uid[i]; - } - - int sumTwoDigits = sum % 256; - - if(sumTwoDigits % 2 == 1) { - sumTwoDigits += 2; - } - - for(size_t i = 0; i < sizeof(xorKey); i++) { - sumHex[i] = sumTwoDigits ^ xorKey[i]; - } -} - -void generateKeyA(const uint8_t* uid, uint8_t uidSize, uint8_t keyA[]) { - uint8_t sumHex[6]; - calculateSumHex(uid, uidSize, sumHex); - uint8_t firstCharacter = (sumHex[0] >> 4) & 0xF; - - if(firstCharacter == 0x2 || firstCharacter == 0x3 || firstCharacter == 0xA || - firstCharacter == 0xB) { - // XOR WITH 0x40 - for(size_t i = 0; i < sizeof(sumHex); i++) { - keyA[i] = 0x40 ^ sumHex[i]; - } - } else if( - firstCharacter == 0x6 || firstCharacter == 0x7 || firstCharacter == 0xE || - firstCharacter == 0xF) { - // XOR WITH 0xC0 - for(size_t i = 0; i < sizeof(sumHex); i++) { - keyA[i] = 0xC0 ^ sumHex[i]; - } - } else { - //Key a is the same as sumHex - for(size_t i = 0; i < sizeof(sumHex); i++) { - keyA[i] = sumHex[i]; - } - } -} - -void generateKeyB(uint8_t keyA[], size_t keyASize, uint8_t keyB[]) { - for(size_t i = 0; i < keyASize; i++) { - keyB[i] = 0xFF ^ keyA[i]; - } -} - -static bool microel_read(Nfc* nfc, NfcDevice* device) { - FURI_LOG_D(TAG, "Entering Microel KDF"); - - furi_assert(nfc); - furi_assert(device); - - bool is_read = false; - - MfClassicData* data = mf_classic_alloc(); - nfc_device_copy_data(device, NfcProtocolMfClassic, data); - - do { - MfClassicType type = MfClassicType1k; - MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); - if(error != MfClassicErrorNone) break; - - //Get UID and check if it is 4 bytes - size_t uid_len; - const uint8_t* uid = mf_classic_get_uid(data, &uid_len); - FURI_LOG_D(TAG, "UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]); - if(uid_len != UID_LENGTH) break; - - // Generate keys - uint8_t keyA[KEY_LENGTH]; - uint8_t keyB[KEY_LENGTH]; - generateKeyA(uid, UID_LENGTH, keyA); - generateKeyB(keyA, KEY_LENGTH, keyB); - - // Check key 0a to verify if it is a microel card - MfClassicKey key = {0}; - nfc_util_num2bytes(nfc_util_bytes2num(keyA, KEY_LENGTH), COUNT_OF(key.data), key.data); - const uint8_t block_num = mf_classic_get_first_block_num_of_sector(0); // This is 0 - MfClassicAuthContext auth_context; - error = - mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); - if(error != MfClassicErrorNone) { - break; - } - - // Save keys generated to stucture - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { - if(microel_1k_keys[i].a == 0x000000000000) { - microel_1k_keys[i].a = nfc_util_bytes2num(keyA, KEY_LENGTH); - } - if(microel_1k_keys[i].b == 0x000000000000) { - microel_1k_keys[i].b = nfc_util_bytes2num(keyB, KEY_LENGTH); - } - } - MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { - nfc_util_num2bytes(microel_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); - FURI_BIT_SET(keys.key_a_mask, i); - nfc_util_num2bytes(microel_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); - FURI_BIT_SET(keys.key_b_mask, i); - } - - error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error == MfClassicErrorNotPresent) { - FURI_LOG_W(TAG, "Failed to read data"); - break; - } - - nfc_device_set_data(device, NfcProtocolMfClassic, data); - - is_read = (error == MfClassicErrorNone); - } while(false); - - mf_classic_free(data); - - return is_read; -} - -static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { - furi_assert(device); - furi_assert(parsed_data); - - const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); - - bool parsed = false; - - do { - //Get UID - size_t uid_len; - const uint8_t* uid = mf_classic_get_uid(data, &uid_len); - if(uid_len != UID_LENGTH) break; - - // Generate key from uid - uint8_t keyA[KEY_LENGTH]; - generateKeyA(uid, UID_LENGTH, keyA); - - // Verify key - MfClassicSectorTrailer* sec_tr = - mf_classic_get_sector_trailer_by_sector(data, verify_sector); - uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); - uint64_t key_for_check_from_array = nfc_util_bytes2num(keyA, KEY_LENGTH); - if(key != key_for_check_from_array) break; - - //Get credit in block number 8 - const uint8_t* temp_ptr = data->block[4].data; - uint16_t balance = (temp_ptr[6] << 8) | (temp_ptr[5]); - uint16_t previus_balance = (data->block[5].data[6] << 8) | (data->block[5].data[5]); - furi_string_cat_printf(parsed_data, "\e#Microel Card\n"); - furi_string_cat_printf(parsed_data, "UID:"); - for(size_t i = 0; i < UID_LENGTH; i++) { - furi_string_cat_printf(parsed_data, " %02X", uid[i]); - } - furi_string_cat_printf( - parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); - furi_string_cat_printf( - parsed_data, - "Previus Credit: %d.%02d E \n", - previus_balance / 100, - previus_balance % 100); - - parsed = true; - } while(false); - - return parsed; -} - -/* Actual implementation of app<>plugin interface */ -static const NfcSupportedCardsPlugin microel_plugin = { - .protocol = NfcProtocolMfClassic, - .verify = - NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 but added verify in read function - .read = microel_read, - .parse = microel_parse, -}; - -/* Plugin descriptor to comply with basic plugin specification */ -static const FlipperAppPluginDescriptor microel_plugin_descriptor = { - .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, - .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, - .entry_point = µel_plugin, -}; - -/* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* microel_plugin_ep() { - return µel_plugin_descriptor; +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#define TAG "Microel" +#define KEY_LENGTH 6 +#define UID_LENGTH 4 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static MfClassicKeyPair microel_1k_keys[] = { + {.a = 0x000000000000, .b = 0x000000000000}, // 000 + {.a = 0x000000000000, .b = 0x000000000000}, // 001 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 002 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 003 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 004 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 005 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 006 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 007 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 008 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 009 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 010 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 011 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 012 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 013 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 014 + {.a = 0xffffffffffff, .b = 0xffffffffffff}, // 015 +}; + +const uint8_t verify_sector = 1; + +void calculateSumHex(const uint8_t* uid, size_t uidSize, uint8_t sumHex[]) { + const uint8_t xorKey[] = {0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9}; + int sum = 0; + + for(size_t i = 0; i < uidSize; i++) { + sum += uid[i]; + } + + int sumTwoDigits = sum % 256; + + if(sumTwoDigits % 2 == 1) { + sumTwoDigits += 2; + } + + for(size_t i = 0; i < sizeof(xorKey); i++) { + sumHex[i] = sumTwoDigits ^ xorKey[i]; + } +} + +void generateKeyA(const uint8_t* uid, uint8_t uidSize, uint8_t keyA[]) { + uint8_t sumHex[6]; + calculateSumHex(uid, uidSize, sumHex); + uint8_t firstCharacter = (sumHex[0] >> 4) & 0xF; + + if(firstCharacter == 0x2 || firstCharacter == 0x3 || firstCharacter == 0xA || + firstCharacter == 0xB) { + // XOR WITH 0x40 + for(size_t i = 0; i < sizeof(sumHex); i++) { + keyA[i] = 0x40 ^ sumHex[i]; + } + } else if( + firstCharacter == 0x6 || firstCharacter == 0x7 || firstCharacter == 0xE || + firstCharacter == 0xF) { + // XOR WITH 0xC0 + for(size_t i = 0; i < sizeof(sumHex); i++) { + keyA[i] = 0xC0 ^ sumHex[i]; + } + } else { + //Key a is the same as sumHex + for(size_t i = 0; i < sizeof(sumHex); i++) { + keyA[i] = sumHex[i]; + } + } +} + +void generateKeyB(uint8_t keyA[], size_t keyASize, uint8_t keyB[]) { + for(size_t i = 0; i < keyASize; i++) { + keyB[i] = 0xFF ^ keyA[i]; + } +} + +static bool microel_read(Nfc* nfc, NfcDevice* device) { + FURI_LOG_D(TAG, "Entering Microel KDF"); + + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + //Get UID and check if it is 4 bytes + size_t uid_len; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + FURI_LOG_D(TAG, "UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]); + if(uid_len != UID_LENGTH) break; + + // Generate keys + uint8_t keyA[KEY_LENGTH]; + uint8_t keyB[KEY_LENGTH]; + generateKeyA(uid, UID_LENGTH, keyA); + generateKeyB(keyA, KEY_LENGTH, keyB); + + // Check key 0a to verify if it is a microel card + MfClassicKey key = {0}; + nfc_util_num2bytes(nfc_util_bytes2num(keyA, KEY_LENGTH), COUNT_OF(key.data), key.data); + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(0); // This is 0 + MfClassicAuthContext auth_context; + error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + break; + } + + // Save keys generated to stucture + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + if(microel_1k_keys[i].a == 0x000000000000) { + microel_1k_keys[i].a = nfc_util_bytes2num(keyA, KEY_LENGTH); + } + if(microel_1k_keys[i].b == 0x000000000000) { + microel_1k_keys[i].b = nfc_util_bytes2num(keyB, KEY_LENGTH); + } + } + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(microel_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(microel_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error == MfClassicErrorNotPresent) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = (error == MfClassicErrorNone); + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + //Get UID + size_t uid_len; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + if(uid_len != UID_LENGTH) break; + + // Generate key from uid + uint8_t keyA[KEY_LENGTH]; + generateKeyA(uid, UID_LENGTH, keyA); + + // Verify key + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, verify_sector); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + uint64_t key_for_check_from_array = nfc_util_bytes2num(keyA, KEY_LENGTH); + if(key != key_for_check_from_array) break; + + //Get credit in block number 8 + const uint8_t* temp_ptr = data->block[4].data; + uint16_t balance = (temp_ptr[6] << 8) | (temp_ptr[5]); + uint16_t previus_balance = (data->block[5].data[6] << 8) | (data->block[5].data[5]); + furi_string_cat_printf(parsed_data, "\e#Microel Card\n"); + furi_string_cat_printf(parsed_data, "UID:"); + for(size_t i = 0; i < UID_LENGTH; i++) { + furi_string_cat_printf(parsed_data, " %02X", uid[i]); + } + furi_string_cat_printf( + parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); + furi_string_cat_printf( + parsed_data, + "Previus Credit: %d.%02d E \n", + previus_balance / 100, + previus_balance % 100); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin microel_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = + NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 but added verify in read function + .read = microel_read, + .parse = microel_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor microel_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = µel_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* microel_plugin_ep() { + return µel_plugin_descriptor; } \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index 16593cc89..7c4a3d19d 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -31,6 +31,13 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSelectProtocol)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSelectProtocol); + } else if( + scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) && + (scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) || + scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) { + const uint32_t possible_scenes[] = {NfcSceneReadMenu, NfcSceneSavedMenu}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + nfc->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneStart); diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index b6ad8144d..579373624 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -31,8 +31,10 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSlixUnlock)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSlixUnlock); - } else if(scene_manager_has_previous_scene( - nfc->scene_manager, NfcSceneMfClassicDictAttack)) { + } else if( + scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) && + (scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) || + scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicDictAttack); } else if(scene_manager_has_previous_scene( diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index 21c0d91db..e33660080 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -32,20 +32,10 @@ void nfc_scene_set_type_on_enter(void* context) { nfc_protocol_support_common_submenu_callback, instance); - FuriString* str = furi_string_alloc(); for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { - furi_string_cat_str(str, nfc_data_generator_get_name(i)); - furi_string_replace_str(str, "Mifare", "MIFARE"); - - submenu_add_item( - submenu, - furi_string_get_cstr(str), - i, - nfc_protocol_support_common_submenu_callback, - instance); - furi_string_reset(str); + const char* name = nfc_data_generator_get_name(i); + submenu_add_item(submenu, name, i, nfc_protocol_support_common_submenu_callback, instance); } - furi_string_free(str); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } @@ -63,15 +53,6 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 4); scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak); consumed = true; - } else if( - (event.event == NfcDataGeneratorTypeMfClassic1k_4b) || - (event.event == NfcDataGeneratorTypeMfClassic1k_7b) || - (event.event == NfcDataGeneratorTypeMfClassic4k_4b) || - (event.event == NfcDataGeneratorTypeMfClassic4k_7b) || - (event.event == NfcDataGeneratorTypeMfClassicMini)) { - nfc_data_generator_fill_data(event.event, instance->nfc_device); - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - consumed = true; } else { nfc_data_generator_fill_data(event.event, instance->nfc_device); scene_manager_set_scene_state( diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index cce7ca525..f09d0a430 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -2,29 +2,6 @@ #include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" -// Sync UID from #UID to block 0 data -void mfclassic_sync_uid(NfcDevice* instance) { - size_t uid_len; - const uint8_t* uid = nfc_device_get_uid(instance, &uid_len); - - MfClassicData* mfc_data = (MfClassicData*)nfc_device_get_data(instance, NfcProtocolMfClassic); - uint8_t* block = mfc_data->block[0].data; - - // Sync UID - for(uint8_t i = 0; i < (uint8_t)uid_len; i++) { - block[i] = uid[i]; - } - - if(uid_len == 4) { - // Calculate BCC - block[uid_len] = 0; - - for(uint8_t i = 0; i < (uint8_t)uid_len; i++) { - block[uid_len] ^= block[i]; - } - } -} - static void nfc_scene_set_uid_byte_input_changed_callback(void* context) { NfcApp* instance = context; // Retrieve previously saved UID length @@ -72,9 +49,6 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { instance->scene_manager, NfcSceneReadMenu); consumed = true; } else { - if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) - mfclassic_sync_uid(instance->nfc_device); - scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); consumed = true; }