mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-12 13:48:35 -07:00
Merge branch 'reborned/nfc_ui_refactor' of git@github.com:RebornedBrain/flipperzero-firmware.git
This commit is contained in:
@@ -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"],
|
||||
|
||||
@@ -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--;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
169
applications/main/nfc/plugins/supported_cards/aime.c
Normal file
169
applications/main/nfc/plugins/supported_cards/aime.c
Normal 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;
|
||||
}
|
||||
130
applications/main/nfc/plugins/supported_cards/mykey.c
Normal file
130
applications/main/nfc/plugins/supported_cards/mykey.c
Normal 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;
|
||||
}
|
||||
155
applications/main/nfc/plugins/supported_cards/umarsh.c
Normal file
155
applications/main/nfc/plugins/supported_cards/umarsh.c
Normal 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;
|
||||
}
|
||||
@@ -1312,3 +1312,7 @@ CE99FBC8BD26
|
||||
# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K)
|
||||
009FB42D98ED
|
||||
002E626E2820
|
||||
|
||||
# Volgograd (Russia) Volna transport cards keys
|
||||
2B787A063D5D
|
||||
D37C8F1793F7
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
119
applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c
Normal file
119
applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user