Merge branch 'reborned/nfc_ui_refactor' of git@github.com:RebornedBrain/flipperzero-firmware.git

This commit is contained in:
RebornedBrain
2023-12-21 14:56:18 +03:00
233 changed files with 3723 additions and 11196 deletions

View File

@@ -13,7 +13,7 @@ App(
"!plugins",
"!nfc_cli.c",
],
fap_libs=["assets"],
fap_libs=["assets", "mbedtls"],
fap_icon="icon.png",
fap_category="NFC",
)
@@ -38,6 +38,15 @@ App(
sources=["plugins/supported_cards/opal.c"],
)
App(
appid="mykey_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="mykey_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/mykey.c"],
)
App(
appid="myki_parser",
apptype=FlipperAppType.PLUGIN,
@@ -74,6 +83,24 @@ App(
sources=["plugins/supported_cards/two_cities.c"],
)
App(
appid="aime_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="aime_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/aime.c"],
)
App(
appid="umarsh_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="umarsh_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/umarsh.c"],
)
App(
appid="nfc_start",
targets=["f7"],

View File

@@ -1,6 +1,6 @@
#include "mf_user_dict.h"
#include <nfc/helpers/nfc_dict.h>
#include <toolbox/keys_dict.h>
#include <nfc/protocols/mf_classic/mf_classic.h>
#include <furi/furi.h>
@@ -15,22 +15,22 @@ struct MfUserDict {
MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) {
MfUserDict* instance = malloc(sizeof(MfUserDict));
NfcDict* dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict);
size_t dict_keys_num = nfc_dict_get_total_keys(dict);
size_t dict_keys_num = keys_dict_get_total_keys(dict);
instance->keys_num = MIN(max_keys_to_load, dict_keys_num);
if(instance->keys_num > 0) {
instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey));
for(size_t i = 0; i < instance->keys_num; i++) {
bool key_loaded =
nfc_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey));
keys_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey));
furi_assert(key_loaded);
}
}
nfc_dict_free(dict);
keys_dict_free(dict);
return instance;
}
@@ -67,13 +67,13 @@ bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) {
furi_assert(index < instance->keys_num);
furi_assert(instance->keys_arr);
NfcDict* dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict);
bool key_delete_success =
nfc_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey));
nfc_dict_free(dict);
keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey));
keys_dict_free(dict);
if(key_delete_success) {
instance->keys_num--;

View File

@@ -1,4 +1,5 @@
#include "nfc_supported_cards.h"
#include "../plugins/supported_cards/nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
@@ -7,22 +8,72 @@
#include <furi.h>
#include <path.h>
#include <m-array.h>
#define TAG "NfcSupportedCards"
#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins")
#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal"
typedef enum {
NfcSupportedCardsPluginFeatureHasVerify = (1U << 0),
NfcSupportedCardsPluginFeatureHasRead = (1U << 1),
NfcSupportedCardsPluginFeatureHasParse = (1U << 2),
} NfcSupportedCardsPluginFeature;
typedef struct {
FuriString* path;
NfcProtocol protocol;
NfcSupportedCardsPluginFeature feature;
} NfcSupportedCardsPluginCache;
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST);
typedef enum {
NfcSupportedCardsLoadStateIdle,
NfcSupportedCardsLoadStateInProgress,
NfcSupportedCardsLoadStateSuccess,
NfcSupportedCardsLoadStateFail,
} NfcSupportedCardsLoadState;
typedef struct {
Storage* storage;
File* directory;
FuriString* file_path;
char file_name[256];
FlipperApplication* app;
} NfcSupportedCards;
} NfcSupportedCardsLoadContext;
static NfcSupportedCards* nfc_supported_cards_alloc() {
struct NfcSupportedCards {
NfcSupportedCardsPluginCache_t plugins_cache_arr;
NfcSupportedCardsLoadState load_state;
NfcSupportedCardsLoadContext* load_context;
};
NfcSupportedCards* nfc_supported_cards_alloc() {
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);
return instance;
}
void nfc_supported_cards_free(NfcSupportedCards* instance) {
furi_assert(instance);
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
furi_string_free(plugin_cache->path);
}
NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
free(instance);
}
static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc() {
NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext));
instance->storage = furi_record_open(RECORD_STORAGE);
instance->directory = storage_file_alloc(instance->storage);
@@ -35,7 +86,7 @@ static NfcSupportedCards* nfc_supported_cards_alloc() {
return instance;
}
static void nfc_supported_cards_free(NfcSupportedCards* instance) {
static void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* instance) {
if(instance->app) {
flipper_application_free(instance->app);
}
@@ -50,7 +101,36 @@ static void nfc_supported_cards_free(NfcSupportedCards* instance) {
}
static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) {
nfc_supported_cards_get_plugin(NfcSupportedCardsLoadContext* instance, FuriString* path) {
furi_assert(instance);
furi_assert(path);
const NfcSupportedCardsPlugin* plugin = NULL;
do {
if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(path)) !=
FlipperApplicationPreloadStatusSuccess)
break;
if(!flipper_application_is_plugin(instance->app)) break;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
break;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) break;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) break;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) break;
plugin = descriptor->entry_point;
} while(false);
return plugin;
}
static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCardsLoadContext* instance) {
const NfcSupportedCardsPlugin* plugin = NULL;
do {
@@ -65,83 +145,137 @@ static const NfcSupportedCardsPlugin*
path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path);
if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) !=
FlipperApplicationPreloadStatusSuccess)
continue;
if(!flipper_application_is_plugin(instance->app)) continue;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
continue;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) continue;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue;
plugin = descriptor->entry_point;
plugin = nfc_supported_cards_get_plugin(instance, instance->file_path);
} while(plugin == NULL); //-V654
return plugin;
}
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) {
void nfc_supported_cards_load_cache(NfcSupportedCards* instance) {
furi_assert(instance);
do {
if((instance->load_state == NfcSupportedCardsLoadStateSuccess) ||
(instance->load_state == NfcSupportedCardsLoadStateFail))
break;
instance->load_context = nfc_supported_cards_load_context_alloc();
while(true) {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(instance->load_context);
if(plugin == NULL) break; //-V547
NfcSupportedCardsPluginCache plugin_cache = {}; //-V779
plugin_cache.path = furi_string_alloc_set(instance->load_context->file_path);
plugin_cache.protocol = plugin->protocol;
if(plugin->verify) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasVerify;
}
if(plugin->read) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasRead;
}
if(plugin->parse) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasParse;
}
NfcSupportedCardsPluginCache_push_back(instance->plugins_cache_arr, plugin_cache);
}
nfc_supported_cards_load_context_free(instance->load_context);
size_t plugins_loaded = NfcSupportedCardsPluginCache_size(instance->plugins_cache_arr);
if(plugins_loaded == 0) {
FURI_LOG_D(TAG, "Plugins not found");
instance->load_state = NfcSupportedCardsLoadStateFail;
} else {
FURI_LOG_D(TAG, "Loaded %zu plugins", plugins_loaded);
instance->load_state = NfcSupportedCardsLoadStateSuccess;
}
} while(false);
}
bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc) {
furi_assert(instance);
furi_assert(device);
furi_assert(nfc);
bool card_read = false;
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
NfcProtocol protocol = nfc_device_get_protocol(device);
do {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779
if(plugin->protocol != protocol) continue;
instance->load_context = nfc_supported_cards_load_context_alloc();
if(plugin->verify) {
if(!plugin->verify(nfc)) continue;
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue;
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;
if(plugin->verify) {
if(!plugin->verify(nfc)) continue;
}
if(plugin->read) {
if(plugin->read(nfc, device)) {
card_read = true;
break;
}
}
}
if(plugin->read) {
card_read = plugin->read(nfc, device);
}
nfc_supported_cards_load_context_free(instance->load_context);
} while(false);
} while(!card_read);
nfc_supported_cards_free(supported_cards);
return card_read;
}
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) {
bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data) {
furi_assert(instance);
furi_assert(device);
furi_assert(parsed_data);
bool parsed = false;
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
bool card_parsed = false;
NfcProtocol protocol = nfc_device_get_protocol(device);
do {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779
if(plugin->protocol != protocol) continue;
instance->load_context = nfc_supported_cards_load_context_alloc();
if(plugin->parse) {
parsed = plugin->parse(device, parsed_data);
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue;
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;
if(plugin->parse) {
if(plugin->parse(device, parsed_data)) {
card_parsed = true;
break;
}
}
}
} while(!parsed);
nfc_supported_cards_load_context_free(instance->load_context);
} while(false);
nfc_supported_cards_free(supported_cards);
return parsed;
return card_parsed;
}

View File

@@ -15,6 +15,34 @@
extern "C" {
#endif
/**
* @brief NfcSupportedCards opaque type definition.
*/
typedef struct NfcSupportedCards NfcSupportedCards;
/**
* @brief Allocate NfcSupportedCards instance.
*
* @return pointer to allocated NfcSupportedCards instance.
*/
NfcSupportedCards* nfc_supported_cards_alloc();
/**
* @brief Delete an NfcSupportedCards instance
*
* @param[in] instance pointer to instance to be deleted.
*/
void nfc_supported_cards_free(NfcSupportedCards* instance);
/**
* @brief Load plugins information to cache.
*
* @note This function must be called before calling read and parse fanctions.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
*/
void nfc_supported_cards_load_cache(NfcSupportedCards* instance);
/**
* @brief Read the card using a custom procedure.
*
@@ -22,13 +50,14 @@ extern "C" {
* try to execute the custom read procedure specified in each. Upon first success,
* no further attempts will be made and the function will return.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in,out] device pointer to a device instance to hold the read data.
* @param[in,out] nfc pointer to an Nfc instance.
* @returns true if the card was successfully read, false otherwise.
*
* @see NfcSupportedCardPluginRead for detailed description.
*/
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc);
/**
* @brief Parse raw data into human-readable representation.
@@ -37,13 +66,17 @@ bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
* try to parse the data according to each implementation. Upon first success,
* no further attempts will be made and the function will return.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in] device pointer to a device instance holding the data is to be parsed.
* @param[out] parsed_data pointer to the string to contain the formatted result.
* @returns true if the card was successfully parsed, false otherwise.
*
* @see NfcSupportedCardPluginParse for detailed description.
*/
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data);
bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data);
#ifdef __cplusplus
}

View File

@@ -12,6 +12,7 @@ enum {
SubmenuIndexUnlock = SubmenuIndexCommonMax,
SubmenuIndexUnlockByReader,
SubmenuIndexUnlockByPassword,
SubmenuIndexWrite,
};
static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) {
@@ -148,6 +149,15 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
SubmenuIndexUnlock,
nfc_protocol_support_common_submenu_callback,
instance);
} else if(
data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||
data->type == MfUltralightTypeNTAG216) {
submenu_add_item(
submenu,
"Write",
SubmenuIndexWrite,
nfc_protocol_support_common_submenu_callback,
instance);
}
}
@@ -188,6 +198,9 @@ static bool
if(event == SubmenuIndexUnlock) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
return true;
} else if(event == SubmenuIndexWrite) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
return true;
}
return false;
}

View File

@@ -8,7 +8,6 @@
#include "nfc_protocol_support.h"
#include "nfc/nfc_app_i.h"
#include "nfc/helpers/nfc_supported_cards.h"
#include "nfc_protocol_support_defs.h"
#include "nfc_protocol_support_gui_common.h"
@@ -157,6 +156,9 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
instance->protocols_detected[instance->protocols_detected_selected_idx];
instance->poller = nfc_poller_alloc(instance->nfc, protocol);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
// Start poller with the appropriate callback
nfc_protocol_support[protocol]->scene_read.on_enter(instance);
@@ -177,7 +179,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
} else if(event.event == NfcCustomEventPollerIncomplete) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc);
bool card_read = nfc_supported_cards_read(
instance->nfc_supported_cards, instance->nfc_device, instance->nfc);
if(card_read) {
notification_message(instance->notifications, &sequence_success);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
@@ -306,7 +309,7 @@ static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {
Widget* widget = instance->widget;
FuriString* temp_str = furi_string_alloc();
if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) {
if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
} else {

View File

@@ -29,7 +29,9 @@ static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, v
NfcApp* instance = context;
const St25tbPollerEvent* st25tb_event = event.event_data;
if(st25tb_event->type == St25tbPollerEventTypeReady) {
if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {
st25tb_event->data->mode_request.mode = St25tbPollerModeRead;
} else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);

View File

@@ -51,6 +51,7 @@ NfcApp* nfc_app_alloc() {
instance->mf_ul_auth = mf_ultralight_auth_alloc();
instance->mfc_key_cache = mf_classic_key_cache_alloc();
instance->nfc_supported_cards = nfc_supported_cards_alloc();
// Nfc device
instance->nfc_device = nfc_device_alloc();
@@ -110,7 +111,6 @@ NfcApp* nfc_app_alloc() {
instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget));
// Dict attack
instance->dict_attack = dict_attack_alloc();
view_dispatcher_add_view(
instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack));
@@ -141,6 +141,7 @@ void nfc_app_free(NfcApp* instance) {
mf_ultralight_auth_free(instance->mf_ul_auth);
mf_classic_key_cache_free(instance->mfc_key_cache);
nfc_supported_cards_free(instance->nfc_supported_cards);
// Nfc device
nfc_device_free(instance->nfc_device);
@@ -339,6 +340,8 @@ bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) {
furi_assert(path);
bool result = false;
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
FuriString* load_path = furi_string_alloc();
if(nfc_has_shadow_file_internal(instance, path)) {
nfc_set_shadow_file_path(path, load_path);
@@ -400,15 +403,16 @@ bool nfc_load_from_file_select(NfcApp* instance) {
browser_options.base_path = NFC_APP_FOLDER;
browser_options.hide_dot_files = true;
// Input events and views are managed by file_browser
bool result = dialog_file_browser_show(
instance->dialogs, instance->file_path, instance->file_path, &browser_options);
bool success = false;
do {
// Input events and views are managed by file_browser
if(!dialog_file_browser_show(
instance->dialogs, instance->file_path, instance->file_path, &browser_options))
break;
success = nfc_load_file(instance, instance->file_path, true);
} while(!success);
if(result) {
result = nfc_load_file(instance, instance->file_path, true);
}
return result;
return success;
}
void nfc_show_loading_popup(void* context, bool show) {

View File

@@ -31,6 +31,7 @@
#include "helpers/mf_user_dict.h"
#include "helpers/mfkey32_logger.h"
#include "helpers/mf_classic_key_cache.h"
#include "helpers/nfc_supported_cards.h"
#include <dialogs/dialogs.h>
#include <storage/storage.h>
@@ -51,7 +52,7 @@
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_data_generator.h>
#include <nfc/helpers/nfc_dict.h>
#include <toolbox/keys_dict.h>
#include <gui/modules/validators.h>
#include <toolbox/path.h>
@@ -79,7 +80,7 @@ typedef enum {
} NfcRpcState;
typedef struct {
NfcDict* dict;
KeysDict* dict;
uint8_t sectors_total;
uint8_t sectors_read;
uint8_t current_sector;
@@ -132,6 +133,7 @@ struct NfcApp {
Mfkey32Logger* mfkey32_logger;
MfUserDict* mf_user_dict;
MfClassicKeyCache* mfc_key_cache;
NfcSupportedCards* nfc_supported_cards;
NfcDevice* nfc_device;
Iso14443_3aData* iso14443_3a_edit_data;

View File

@@ -0,0 +1,169 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Aime"
static const uint64_t aime_key = 0x574343467632;
bool aime_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t verify_sector = 0;
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
MfClassicKey key = {};
nfc_util_num2bytes(aime_key, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool aime_read(Nfc* nfc, NfcDevice* device) {
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;
data->type = type;
MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(aime_key, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(aime_key, 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 != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
} while(false);
mf_classic_free(data);
return is_read;
}
static bool aime_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6);
if(key != aime_key) break;
// Aime Magic is stored at block 1, starts from byte 0, len 4 bytes
const uint8_t* aime_magic = &data->block[1].data[0];
// verify aime magic
if(aime_magic[0] != 'S' || aime_magic[1] != 'B' || aime_magic[2] != 'S' ||
aime_magic[3] != 'D')
break;
// Aime checksum is stored at block 1, starts from byte 13, len 3 bytes
// seems like only old games checks this? e.g., old versions of Chunithm
const uint8_t* aime_checksum = &data->block[1].data[13];
// Aime access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes
const uint8_t* aime_accesscode = &data->block[2].data[6];
char aime_accesscode_str[24 + 1];
snprintf(
aime_accesscode_str,
sizeof(aime_accesscode_str),
"%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x",
aime_accesscode[0],
aime_accesscode[1],
aime_accesscode[2],
aime_accesscode[3],
aime_accesscode[4],
aime_accesscode[5],
aime_accesscode[6],
aime_accesscode[7],
aime_accesscode[8],
aime_accesscode[9]);
// validate decimal hex representation
bool code_is_hex = true;
for(int i = 0; i < 24; i++) {
if(aime_accesscode_str[i] == ' ') continue;
if(aime_accesscode_str[i] < '0' || aime_accesscode_str[i] > '9') {
code_is_hex = false;
break;
}
}
if(!code_is_hex) break;
// Note: Aime access code has some other self-check algorithms that are not public.
// This parser does not try to verify the number.
furi_string_printf(
parsed_data,
"\e#Aime Card\nAccess Code: \n%s\nChecksum: %02X%02X%02X\n",
aime_accesscode_str,
aime_checksum[0],
aime_checksum[1],
aime_checksum[2]);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin aime_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = aime_verify,
.read = aime_read,
.parse = aime_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor aime_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &aime_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* aime_plugin_ep() {
return &aime_plugin_descriptor;
}

View File

@@ -0,0 +1,130 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <machine/endian.h>
#include <nfc/protocols/st25tb/st25tb.h>
#define TAG "MyKey"
const uint32_t blankBlock18 = 0x480FCD8F, blankBlock19 = 0x070082C0;
static bool mykey_is_blank(const St25tbData* data) {
return data->blocks[0x18] == blankBlock18 && data->blocks[0x19] == blankBlock19;
}
static bool mykey_has_lockid(const St25tbData* data) {
return (data->blocks[5] & 0xFF) == 0x7F;
}
static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);
if(data->type != St25tbType04k && data->type != St25tbTypeX4k) {
FURI_LOG_D(TAG, "bad type");
return false;
}
for(int i = 0; i < 5; i++) {
if(data->blocks[i] != 0xFFFFFFFF) {
FURI_LOG_D(TAG, "bad otp block %d", i);
return false;
}
}
if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) {
FURI_LOG_D(TAG, "bad mfg date");
return false;
}
if(data->system_otp_block != 0xFEFFFFFF) {
FURI_LOG_D(TAG, "bad sys otp block");
return false;
}
furi_string_cat(parsed_data, "\e#MyKey\n");
if(data->blocks[6] == 0) { // Tag is actually a MyKey but it has been bricked by a reader
furi_string_cat(parsed_data, "\e#Bricked!\nBlock 6 is 0!");
return true;
}
bool is_blank = mykey_is_blank(data);
furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", (uint32_t)__bswap32(data->blocks[7]));
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no");
furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no");
uint32_t block8 = data->blocks[8];
furi_string_cat_printf(
parsed_data,
"Prod. date: %02lX/%02lX/%04lX",
block8 >> 16 & 0xFF,
block8 >> 8 & 0xFF,
0x2000 + (block8 & 0xFF));
if(!is_blank) {
furi_string_cat_printf(
parsed_data, "\nOp. count: %zu\n", (size_t)__bswap32(data->blocks[0x12] & 0xFFFFFF00));
uint32_t block3C = data->blocks[0x3C];
if(block3C == 0xFFFFFFFF) {
furi_string_cat(parsed_data, "No history available!");
} else {
block3C ^= data->blocks[0x07];
uint32_t startingOffset = ((block3C & 0x30000000) >> 28) |
((block3C & 0x00100000) >> 18);
furi_check(startingOffset < 8); //-V547
for(int txnOffset = 8; txnOffset > 0; txnOffset--) {
uint32_t txnBlock =
__bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]);
if(txnBlock == 0xFFFFFFFF) {
break;
}
uint8_t day = txnBlock >> 27;
uint8_t month = txnBlock >> 23 & 0xF;
uint16_t year = 2000 + (txnBlock >> 16 & 0x7F);
uint16_t credit = txnBlock & 0xFFFF;
if(txnOffset == 8) {
furi_string_cat_printf(
parsed_data, "Current credit: %d.%02d euros\n", credit / 100, credit % 100);
furi_string_cat(parsed_data, "Op. history (newest first):");
}
furi_string_cat_printf(
parsed_data,
"\n %02d/%02d/%04d %d.%02d",
day,
month,
year,
credit / 100,
credit % 100);
}
}
}
return true;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin mykey_plugin = {
.protocol = NfcProtocolSt25tb,
.verify = NULL,
.read = NULL,
.parse = mykey_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor mykey_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &mykey_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* mykey_plugin_ep() {
return &mykey_plugin_descriptor;
}

View File

@@ -0,0 +1,155 @@
/*
* Parser for Umarsh card (Russia).
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
* Thanks https://github.com/krolchonok for the provided dumps and their analysis
*
* Note: All meaningful data is stored in sectors 0, 8 and 12, reading data
* from which is possible only with the B key. The key B for these sectors
* is unique for each card. To get it, you should use a nested attack.
* More info about Umarsh cards: https://github.com/metrodroid/metrodroid/wiki/Umarsh
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core/core_defines.h"
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdbool.h>
#include <stdint.h>
#include <furi_hal_rtc.h>
#define TAG "Umarsh"
bool parse_datetime(uint16_t date, FuriHalRtcDateTime* result) {
result->year = 2000 + (date >> 9);
result->month = date >> 5 & 0x0F;
result->day = date & 0x1F;
return (date != 0);
}
static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify card type
if(data->type != MfClassicType1k) break;
const uint8_t ticket_sector = 8;
const uint8_t ticket_sector_start_block_number =
mf_classic_get_first_block_num_of_sector(ticket_sector);
// Validate specific for Umarsh ticket sector header
const uint8_t* block_start_ptr = &data->block[ticket_sector_start_block_number].data[0];
const uint32_t header_part_0 = nfc_util_bytes2num(block_start_ptr, 4);
const uint32_t header_part_1 = nfc_util_bytes2num(block_start_ptr + 4, 4);
if((header_part_0 + header_part_1) != 0xFFFFFFFF) break;
// Data parsing from block 1
block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0];
const uint16_t expiry_date = nfc_util_bytes2num(block_start_ptr + 1, 2);
const uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) |
(block_start_ptr[12] & 0x0F);
const uint8_t refill_counter = nfc_util_bytes2num(block_start_ptr + 7, 1);
const uint32_t card_number = nfc_util_bytes2num(block_start_ptr + 8, 4) & 0x3FFFFFFF;
if(card_number == 0) break;
// Data parsing from block 2
block_start_ptr = &data->block[ticket_sector_start_block_number + 2].data[0];
const uint16_t valid_to = nfc_util_bytes2num(block_start_ptr, 2);
const uint32_t terminal_number = nfc_util_bytes2num(block_start_ptr + 3, 3);
const uint16_t last_refill_date = nfc_util_bytes2num(block_start_ptr + 6, 2);
const uint16_t balance_rub = (nfc_util_bytes2num(block_start_ptr + 8, 2)) & 0x7FFF;
const uint8_t balance_kop = nfc_util_bytes2num(block_start_ptr + 10, 1) & 0x7F;
FuriHalRtcDateTime expiry_datetime;
bool is_expiry_datetime_valid = parse_datetime(expiry_date, &expiry_datetime);
FuriHalRtcDateTime valid_to_datetime;
bool is_valid_to_datetime_valid = parse_datetime(valid_to, &valid_to_datetime);
FuriHalRtcDateTime last_refill_datetime;
bool is_last_refill_datetime_valid =
parse_datetime(last_refill_date, &last_refill_datetime);
furi_string_cat_printf(
parsed_data,
"\e#Umarsh\nCard number: %lu\nRegion: %02u\nTerminal number: %lu\nRefill counter: %u\nBalance: %u.%02u RUR",
card_number,
region_number,
terminal_number,
refill_counter,
balance_rub,
balance_kop);
if(is_expiry_datetime_valid)
furi_string_cat_printf(
parsed_data,
"\nExpires: %02u.%02u.%u",
expiry_datetime.day,
expiry_datetime.month,
expiry_datetime.year);
if(is_valid_to_datetime_valid)
furi_string_cat_printf(
parsed_data,
"\nValid to: %02u.%02u.%u",
valid_to_datetime.day,
valid_to_datetime.month,
valid_to_datetime.year);
if(is_last_refill_datetime_valid)
furi_string_cat_printf(
parsed_data,
"\nLast refill: %02u.%02u.%u",
last_refill_datetime.day,
last_refill_datetime.month,
last_refill_datetime.year);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin umarsh_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = umarsh_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor umarsh_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &umarsh_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* umarsh_plugin_ep() {
return &umarsh_plugin_descriptor;
}

View File

@@ -1312,3 +1312,7 @@ CE99FBC8BD26
# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K)
009FB42D98ED
002E626E2820
# Volgograd (Russia) Volna transport cards keys
2B787A063D5D
D37C8F1793F7

View File

@@ -24,6 +24,10 @@ ADD_SCENE(nfc, field, Field)
ADD_SCENE(nfc, retry_confirm, RetryConfirm)
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite)
ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess)
ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail)
ADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard)
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)

View File

@@ -11,6 +11,7 @@ void nfc_scene_delete_success_on_enter(void* context) {
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_delete_success_popup_callback);

View File

@@ -38,6 +38,7 @@ bool nfc_scene_detect_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) {
if(instance->protocols_detected_num > 1) {
notification_message(instance->notifications, &sequence_single_vibro);
scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol);
} else {
scene_manager_next_scene(instance->scene_manager, NfcSceneRead);

View File

@@ -45,11 +45,7 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexMfClassicKeys) {
if(nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);
} else {
scene_manager_previous_scene(instance->scene_manager);
}
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);
consumed = true;
} else if(event.event == SubmenuIndexMfUltralightUnlock) {
mf_ultralight_auth_reset(instance->mf_ul_auth);

View File

@@ -41,7 +41,8 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) {
MfClassicKey key = {};
if(nfc_dict_get_next_key(instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {
if(keys_dict_get_next_key(
instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {
mfc_event->data->key_request_data.key = key;
mfc_event->data->key_request_data.key_provided = true;
instance->nfc_dict_context.dict_keys_current++;
@@ -60,7 +61,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
nfc_dict_rewind(instance->nfc_dict_context.dict);
keys_dict_rewind(instance->nfc_dict_context.dict);
instance->nfc_dict_context.dict_keys_current = 0;
instance->nfc_dict_context.current_sector =
mfc_event->data->next_sector_data.current_sector;
@@ -79,7 +80,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) {
nfc_dict_rewind(instance->nfc_dict_context.dict);
keys_dict_rewind(instance->nfc_dict_context.dict);
instance->nfc_dict_context.is_key_attack = false;
instance->nfc_dict_context.dict_keys_current = 0;
view_dispatcher_send_custom_event(
@@ -124,15 +125,15 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(state == DictAttackStateUserDictInProgress) {
do {
if(!nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
state = DictAttackStateSystemDictInProgress;
break;
}
instance->nfc_dict_context.dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
if(nfc_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
nfc_dict_free(instance->nfc_dict_context.dict);
instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
keys_dict_free(instance->nfc_dict_context.dict);
state = DictAttackStateSystemDictInProgress;
break;
}
@@ -141,13 +142,13 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
} while(false);
}
if(state == DictAttackStateSystemDictInProgress) {
instance->nfc_dict_context.dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey));
instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary");
}
instance->nfc_dict_context.dict_keys_total =
nfc_dict_get_total_keys(instance->nfc_dict_context.dict);
keys_dict_get_total_keys(instance->nfc_dict_context.dict);
dict_attack_set_total_dict_keys(
instance->dict_attack, instance->nfc_dict_context.dict_keys_total);
instance->nfc_dict_context.dict_keys_current = 0;
@@ -185,7 +186,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
if(state == DictAttackStateUserDictInProgress) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
nfc_dict_free(instance->nfc_dict_context.dict);
keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicDictAttack,
@@ -215,7 +216,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
nfc_dict_free(instance->nfc_dict_context.dict);
keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicDictAttack,
@@ -253,7 +254,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
nfc_dict_free(instance->nfc_dict_context.dict);
keys_dict_free(instance->nfc_dict_context.dict);
instance->nfc_dict_context.current_sector = 0;
instance->nfc_dict_context.sectors_total = 0;

View File

@@ -14,20 +14,20 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
// Load flipper dict keys total
uint32_t flipper_dict_keys_total = 0;
NfcDict* dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey));
KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
if(dict) {
flipper_dict_keys_total = nfc_dict_get_total_keys(dict);
nfc_dict_free(dict);
flipper_dict_keys_total = keys_dict_get_total_keys(dict);
keys_dict_free(dict);
}
// Load user dict keys total
uint32_t user_dict_keys_total = 0;
dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(dict) {
user_dict_keys_total = nfc_dict_get_total_keys(dict);
nfc_dict_free(dict);
user_dict_keys_total = keys_dict_get_total_keys(dict);
keys_dict_free(dict);
}
FuriString* temp_str = furi_string_alloc();

View File

@@ -29,23 +29,23 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) {
// Add key to dict
NfcDict* dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict);
MfClassicKey key = {};
memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey));
if(nfc_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) {
if(keys_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) {
scene_manager_next_scene(
instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
} else if(nfc_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {
} else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
dolphin_deed(DolphinDeedNfcMfcAdd);
} else {
scene_manager_previous_scene(instance->scene_manager);
}
nfc_dict_free(dict);
keys_dict_free(dict);
consumed = true;
}
}

View File

@@ -52,11 +52,12 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
bool consumed = false;
nfc->protocols_detected[0] = nfc_device_get_protocol(nfc->nfc_device);
MfUltralightAuthType type = nfc->mf_ul_auth->type;
if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultRight) {
const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight};
nfc_app_set_detected_protocols(nfc, mfu_protocol, COUNT_OF(mfu_protocol));
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
dolphin_deed(DolphinDeedNfcRead);
consumed = true;

View File

@@ -0,0 +1,119 @@
#include "../nfc_app_i.h"
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
enum {
NfcSceneMfUltralightWriteStateCardSearch,
NfcSceneMfUltralightWriteStateCardFound,
};
NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolMfUltralight);
NfcCommand command = NfcCommandContinue;
NfcApp* instance = context;
MfUltralightPollerEvent* mfu_event = event.event_data;
if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) {
mfu_event->data->poller_mode = MfUltralightPollerModeWrite;
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {
mfu_event->data->auth_context.skip_auth = true;
} else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) {
mfu_event->data->write_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
} else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
command = NfcCommandStop;
} else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) {
command = NfcCommandStop;
} else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
}
return command;
}
static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) {
Popup* popup = instance->popup;
popup_reset(popup);
uint32_t state =
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite);
if(state == NfcSceneMfUltralightWriteStateCardSearch) {
popup_set_text(
instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
} else {
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
popup_set_icon(popup, 12, 23, &A_Loading_24);
}
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
}
void nfc_scene_mf_ultralight_write_on_enter(void* context) {
NfcApp* instance = context;
dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfUltralightWrite,
NfcSceneMfUltralightWriteStateCardSearch);
nfc_scene_mf_ultralight_write_setup_view(instance);
// Setup and start worker
FURI_LOG_D("WMFU", "Card searching...");
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance);
nfc_blink_emulate_start(instance);
}
bool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) {
NfcApp* instance = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventCardDetected) {
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfUltralightWrite,
NfcSceneMfUltralightWriteStateCardFound);
nfc_scene_mf_ultralight_write_setup_view(instance);
consumed = true;
} else if(event.event == NfcCustomEventWrongCard) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard);
consumed = true;
} else if(event.event == NfcCustomEventPollerSuccess) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess);
consumed = true;
} else if(event.event == NfcCustomEventPollerFailure) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_ultralight_write_on_exit(void* context) {
NfcApp* instance = context;
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfUltralightWrite,
NfcSceneMfUltralightWriteStateCardSearch);
// Clear view
popup_reset(instance->popup);
nfc_blink_stop(instance);
}

View File

@@ -0,0 +1,67 @@
#include "../nfc_app_i.h"
void nfc_scene_mf_ultralight_write_fail_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
NfcApp* instance = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
}
}
void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) {
NfcApp* instance = context;
Widget* widget = instance->widget;
notification_message(instance->notifications, &sequence_error);
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
widget_add_string_multiline_element(
widget,
7,
17,
AlignLeft,
AlignTop,
FontSecondary,
"Card protected by\npassword, AUTH0\nor lock bits");
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Finish",
nfc_scene_mf_ultralight_write_fail_widget_callback,
instance);
// Setup and start worker
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
}
static bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) {
bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);
uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu;
return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id);
}
bool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) {
NfcApp* instance = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance);
}
return consumed;
}
void nfc_scene_mf_ultralight_write_fail_on_exit(void* context) {
NfcApp* instance = context;
widget_reset(instance->widget);
}

View File

@@ -0,0 +1,43 @@
#include "../nfc_app_i.h"
void nfc_scene_mf_ultralight_write_success_popup_callback(void* context) {
NfcApp* instance = context;
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_mf_ultralight_write_success_on_enter(void* context) {
NfcApp* instance = context;
dolphin_deed(DolphinDeedNfcSave);
notification_message(instance->notifications, &sequence_success);
Popup* popup = instance->popup;
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, instance);
popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
}
bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) {
NfcApp* instance = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, NfcSceneSavedMenu);
}
}
return consumed;
}
void nfc_scene_mf_ultralight_write_success_on_exit(void* context) {
NfcApp* instance = context;
// Clear view
popup_reset(instance->popup);
}

View File

@@ -0,0 +1,58 @@
#include "../nfc_app_i.h"
void nfc_scene_mf_ultralight_wrong_card_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
NfcApp* instance = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
}
}
void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) {
NfcApp* instance = context;
Widget* widget = instance->widget;
notification_message(instance->notifications, &sequence_error);
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
widget_add_string_multiline_element(
widget,
4,
17,
AlignLeft,
AlignTop,
FontSecondary,
"Card of the same\ntype should be\n presented");
//"Data management\nis only possible\nwith card of same type");
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Retry",
nfc_scene_mf_ultralight_wrong_card_widget_callback,
instance);
// Setup and start worker
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) {
NfcApp* instance = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(instance->scene_manager);
}
}
return consumed;
}
void nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) {
NfcApp* instance = context;
widget_reset(instance->widget);
}

View File

@@ -10,7 +10,8 @@ void nfc_scene_save_success_on_enter(void* context) {
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 15, 5, &I_DolphinSaved_113x58);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_save_success_popup_callback);

View File

@@ -21,7 +21,6 @@ void nfc_scene_select_protocol_on_enter(void* context) {
} else {
prefix = "Read as";
submenu_set_header(submenu, "Multi-protocol card");
notification_message(instance->notifications, &sequence_single_vibro);
}
for(uint32_t i = 0; i < instance->protocols_detected_num; i++) {

View File

@@ -1,6 +1,5 @@
#include "nfc/nfc_app_i.h"
#include "nfc/helpers/nfc_supported_cards.h"
#include "nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h"
void nfc_scene_supported_card_on_enter(void* context) {
@@ -8,7 +7,7 @@ void nfc_scene_supported_card_on_enter(void* context) {
FuriString* temp_str = furi_string_alloc();
if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) {
if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
widget_add_button_element(

View File

@@ -125,7 +125,6 @@ void dict_attack_reset(DictAttack* instance) {
instance->view,
DictAttackViewModel * model,
{
model->card_detected = false;
model->sectors_total = 0;
model->sectors_read = 0;
model->current_sector = 0;