1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Merge branch 'mergingnfctyp4' into dev [ci skip]

This commit is contained in:
MX
2025-06-30 20:48:53 +03:00
125 changed files with 6323 additions and 1279 deletions

View File

@@ -1,5 +1,8 @@
#include "gallagher/gallagher_util.h" #include "gallagher/gallagher_util.h"
#include "mosgortrans/mosgortrans_util.h" #include "mosgortrans/mosgortrans_util.h"
#include "../nfc_app_i.h"
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
#include "../helpers/protocol_support/nfc_protocol_support_unlock_helper.h"
/* /*
* A list of app's private functions and objects to expose for plugins. * A list of app's private functions and objects to expose for plugins.
@@ -22,4 +25,20 @@ static constexpr auto nfc_app_api_table = sort(create_array_t<sym_entry>(
(FuriString * str, (FuriString * str,
const char* name, const char* name,
uint8_t prefix_separator_cnt, uint8_t prefix_separator_cnt,
uint8_t suffix_separator_cnt)))); uint8_t suffix_separator_cnt)),
API_METHOD(
nfc_append_filename_string_when_present,
void,
(NfcApp * instance, FuriString* string)),
API_METHOD(nfc_protocol_support_common_submenu_callback, void, (void* context, uint32_t index)),
API_METHOD(
nfc_protocol_support_common_widget_callback,
void,
(GuiButtonType result, InputType type, void* context)),
API_METHOD(nfc_protocol_support_common_on_enter_empty, void, (NfcApp * instance)),
API_METHOD(
nfc_protocol_support_common_on_event_empty,
bool,
(NfcApp * instance, SceneManagerEvent event)),
API_METHOD(nfc_unlock_helper_setup_from_state, void, (NfcApp * instance)),
API_METHOD(nfc_unlock_helper_card_detected_handler, void, (NfcApp * instance))));

View File

@@ -18,6 +18,205 @@ App(
fap_category="NFC", fap_category="NFC",
) )
# Protocol support plugins
App(
appid="nfc_iso14443_3a",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_iso14443_3a_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/iso14443_3a/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_iso14443_3b",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_iso14443_3b_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/iso14443_3b/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_iso14443_4a",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_iso14443_4a_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/iso14443_4a/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_iso14443_4b",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_iso14443_4b_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/iso14443_4b/*.c",
"helpers/protocol_support/iso14443_3b/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_iso15693_3",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_iso15693_3_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/iso15693_3/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_felica",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_felica_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/felica/*.c",
"helpers/felica_*.c",
],
fal_embedded=True,
)
App(
appid="nfc_mf_ultralight",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_mf_ultralight_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/mf_ultralight/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
"helpers/mf_ultralight_*.c",
],
fap_libs=["mbedtls"],
fal_embedded=True,
)
App(
appid="nfc_mf_classic",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_mf_classic_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/mf_classic/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
"helpers/mf_classic_*.c",
],
fal_embedded=True,
)
App(
appid="nfc_mf_plus",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_mf_plus_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/mf_plus/*.c",
"helpers/protocol_support/iso14443_4a/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_mf_desfire",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_mf_desfire_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/mf_desfire/*.c",
"helpers/protocol_support/iso14443_4a/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_slix",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_slix_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/slix/*.c",
"helpers/protocol_support/iso15693_3/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_st25tb",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_st25tb_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/st25tb/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_ntag4xx",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_ntag4xx_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/ntag4xx/*.c",
"helpers/protocol_support/iso14443_4a/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_type_4_tag",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_type_4_tag_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/type_4_tag/*.c",
"helpers/protocol_support/iso14443_4a/*.c",
"helpers/protocol_support/iso14443_3a/*.c",
],
fal_embedded=True,
)
App(
appid="nfc_emv",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="nfc_emv_ep",
requires=["nfc"],
sources=[
"helpers/protocol_support/emv/*.c",
],
fal_embedded=True,
)
# Parser plugins # Parser plugins
App( App(
@@ -29,6 +228,15 @@ App(
sources=["plugins/supported_cards/all_in_one.c"], sources=["plugins/supported_cards/all_in_one.c"],
) )
App(
appid="smartrider_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="smartrider_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/smartrider.c"],
)
App( App(
appid="microel_parser", appid="microel_parser",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
@@ -254,6 +462,7 @@ App(
requires=["nfc"], requires=["nfc"],
sources=["plugins/supported_cards/ndef.c"], sources=["plugins/supported_cards/ndef.c"],
) )
App( App(
appid="ndef_mfc_parser", appid="ndef_mfc_parser",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
@@ -274,6 +483,16 @@ App(
sources=["plugins/supported_cards/ndef.c"], sources=["plugins/supported_cards/ndef.c"],
) )
App(
appid="ndef_t4t_parser",
apptype=FlipperAppType.PLUGIN,
cdefines=[("NDEF_PROTO", "NDEF_PROTO_T4T")],
entry_point="ndef_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/ndef.c"],
)
App( App(
appid="itso_parser", appid="itso_parser",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
@@ -320,6 +539,33 @@ App(
sources=["plugins/supported_cards/disney_infinity.c"], sources=["plugins/supported_cards/disney_infinity.c"],
) )
App(
appid="sonicare_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="sonicare_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/sonicare.c"],
)
App(
appid="csc_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="csc_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/csc.c"],
)
App(
appid="ventra_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="ventra_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/ventra.c"],
)
App( App(
appid="cli_nfc", appid="cli_nfc",
targets=["f7"], targets=["f7"],

View File

@@ -13,6 +13,7 @@ typedef enum {
NfcCustomEventCardLost, NfcCustomEventCardLost,
NfcCustomEventViewExit, NfcCustomEventViewExit,
NfcCustomEventRetry,
NfcCustomEventWorkerExit, NfcCustomEventWorkerExit,
NfcCustomEventWorkerUpdate, NfcCustomEventWorkerUpdate,
NfcCustomEventWrongCard, NfcCustomEventWrongCard,
@@ -30,4 +31,6 @@ typedef enum {
NfcCustomEventPollerFailure, NfcCustomEventPollerFailure,
NfcCustomEventListenerUpdate, NfcCustomEventListenerUpdate,
NfcCustomEventEmulationTimeExpired,
} NfcCustomEvent; } NfcCustomEvent;

View File

@@ -1,11 +1,9 @@
#include "nfc_supported_cards.h" #include "nfc_supported_cards.h"
#include "../api/nfc_app_api_interface.h"
#include "../plugins/supported_cards/nfc_supported_card_plugin.h" #include "../plugins/supported_cards/nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h> #include <flipper_application/flipper_application.h>
#include <flipper_application/plugins/plugin_manager.h> #include <flipper_application/plugins/plugin_manager.h>
#include <flipper_application/plugins/composite_resolver.h>
#include <loader/firmware_api/firmware_api.h> #include <loader/firmware_api/firmware_api.h>
#include <furi.h> #include <furi.h>
@@ -52,12 +50,9 @@ struct NfcSupportedCards {
NfcSupportedCardsLoadContext* load_context; NfcSupportedCardsLoadContext* load_context;
}; };
NfcSupportedCards* nfc_supported_cards_alloc(void) { NfcSupportedCards* nfc_supported_cards_alloc(CompositeApiResolver* api_resolver) {
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
instance->api_resolver = api_resolver;
instance->api_resolver = composite_api_resolver_alloc();
composite_api_resolver_add(instance->api_resolver, firmware_api_interface);
composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface);
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr); NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);
@@ -76,7 +71,6 @@ void nfc_supported_cards_free(NfcSupportedCards* instance) {
} }
NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr); NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
composite_api_resolver_free(instance->api_resolver);
free(instance); free(instance);
} }

View File

@@ -7,6 +7,7 @@
#pragma once #pragma once
#include <core/string.h> #include <core/string.h>
#include <flipper_application/plugins/composite_resolver.h>
#include <nfc/nfc.h> #include <nfc/nfc.h>
#include <nfc/nfc_device.h> #include <nfc/nfc_device.h>
@@ -25,7 +26,7 @@ typedef struct NfcSupportedCards NfcSupportedCards;
* *
* @return pointer to allocated NfcSupportedCards instance. * @return pointer to allocated NfcSupportedCards instance.
*/ */
NfcSupportedCards* nfc_supported_cards_alloc(void); NfcSupportedCards* nfc_supported_cards_alloc(CompositeApiResolver* api_resolver);
/** /**
* @brief Delete an NfcSupportedCards instance * @brief Delete an NfcSupportedCards instance

View File

@@ -128,4 +128,11 @@ const NfcProtocolSupportBase nfc_protocol_support_emv = {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(emv, NfcProtocolEmv);

View File

@@ -133,15 +133,6 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
furi_string_free(temp_str); furi_string_free(temp_str);
} }
static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) { static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica); const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data); instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data);
@@ -201,7 +192,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = {
.scene_saved_menu = .scene_saved_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_felica, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_save_name = .scene_save_name =
{ {
@@ -213,4 +204,11 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = {
.on_enter = nfc_scene_emulate_on_enter_felica, .on_enter = nfc_scene_emulate_on_enter_felica,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(felica, NfcProtocolFelica);

View File

@@ -67,21 +67,22 @@ static NfcCommand
furi_assert(event.protocol == NfcProtocolIso14443_3a); furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(event.event_data); furi_assert(event.event_data);
NfcApp* nfc = context; NfcApp* instance = context;
Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data; Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;
if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(nfc->text_box_store, "R:"); furi_string_cat_printf(instance->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer);
i++) { i++) {
furi_string_cat_printf( furi_string_cat_printf(
nfc->text_box_store, instance->text_box_store,
" %02X", " %02X",
bit_buffer_get_byte(iso14443_3a_event->data->buffer, i)); bit_buffer_get_byte(iso14443_3a_event->data->buffer, i));
} }
furi_string_push_back(nfc->text_box_store, '\n'); furi_string_push_back(instance->text_box_store, '\n');
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventListenerUpdate);
} }
} }
@@ -97,15 +98,6 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) {
instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance);
} }
static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
@@ -122,7 +114,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
.scene_read_menu = .scene_read_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_read_menu_on_event_iso14443_3a, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_read_success = .scene_read_success =
{ {
@@ -144,4 +136,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
.on_enter = nfc_scene_emulate_on_enter_iso14443_3a, .on_enter = nfc_scene_emulate_on_enter_iso14443_3a,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_3a, NfcProtocolIso14443_3a);

View File

@@ -60,19 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) {
furi_string_free(temp_str); furi_string_free(temp_str);
} }
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) {
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
}
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
.features = NfcProtocolFeatureNone, .features = NfcProtocolFeatureNone,
@@ -99,7 +86,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
.scene_saved_menu = .scene_saved_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_iso14443_3b, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_save_name = .scene_save_name =
{ {
@@ -111,4 +98,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_3b, NfcProtocolIso14443_3b);

View File

@@ -1,7 +0,0 @@
#pragma once
#include <nfc/protocols/iso14443_3b/iso14443_3b.h>
#include "iso14443_3b.h"
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event);

View File

@@ -70,21 +70,22 @@ NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event
furi_assert(event.protocol == NfcProtocolIso14443_4a); furi_assert(event.protocol == NfcProtocolIso14443_4a);
furi_assert(event.event_data); furi_assert(event.event_data);
NfcApp* nfc = context; NfcApp* instance = context;
Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data; Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data;
if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) {
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(nfc->text_box_store, "R:"); furi_string_cat_printf(instance->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer); for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer);
i++) { i++) {
furi_string_cat_printf( furi_string_cat_printf(
nfc->text_box_store, instance->text_box_store,
" %02X", " %02X",
bit_buffer_get_byte(iso14443_4a_event->data->buffer, i)); bit_buffer_get_byte(iso14443_4a_event->data->buffer, i));
} }
furi_string_push_back(nfc->text_box_store, '\n'); furi_string_push_back(instance->text_box_store, '\n');
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventListenerUpdate);
} }
} }
@@ -100,15 +101,6 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) {
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
} }
static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
@@ -125,7 +117,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
.scene_read_menu = .scene_read_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_read_menu_on_event_iso14443_4a, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_read_success = .scene_read_success =
{ {
@@ -147,4 +139,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
.on_enter = nfc_scene_emulate_on_enter_iso14443_4a, .on_enter = nfc_scene_emulate_on_enter_iso14443_4a,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_4a, NfcProtocolIso14443_4a);

View File

@@ -7,7 +7,6 @@
#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h" #include "../nfc_protocol_support_gui_common.h"
#include "../iso14443_3b/iso14443_3b_i.h"
static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) { static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device; const NfcDevice* device = instance->nfc_device;
@@ -61,23 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_4b(NfcApp* instance) {
furi_string_free(temp_str); furi_string_free(temp_str);
} }
static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) {
UNUSED(instance);
}
static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
return true;
}
return false;
}
static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
}
const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
.features = NfcProtocolFeatureNone, .features = NfcProtocolFeatureNone,
@@ -94,7 +76,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
.scene_read_menu = .scene_read_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_read_menu_on_event_iso14443_4b, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_read_success = .scene_read_success =
{ {
@@ -103,8 +85,8 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
}, },
.scene_saved_menu = .scene_saved_menu =
{ {
.on_enter = nfc_scene_saved_menu_on_enter_iso14443_4b, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_iso14443_4b, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_save_name = .scene_save_name =
{ {
@@ -116,4 +98,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_4b, NfcProtocolIso14443_4b);

View File

@@ -80,20 +80,21 @@ static NfcCommand
furi_assert(event.protocol == NfcProtocolIso15693_3); furi_assert(event.protocol == NfcProtocolIso15693_3);
furi_assert(event.event_data); furi_assert(event.event_data);
NfcApp* nfc = context; NfcApp* instance = context;
Iso15693_3ListenerEvent* iso15693_3_event = event.event_data; Iso15693_3ListenerEvent* iso15693_3_event = event.event_data;
if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) {
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(nfc->text_box_store, "R:"); furi_string_cat_printf(instance->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) { for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) {
furi_string_cat_printf( furi_string_cat_printf(
nfc->text_box_store, instance->text_box_store,
" %02X", " %02X",
bit_buffer_get_byte(iso15693_3_event->data->buffer, i)); bit_buffer_get_byte(iso15693_3_event->data->buffer, i));
} }
furi_string_push_back(nfc->text_box_store, '\n'); furi_string_push_back(instance->text_box_store, '\n');
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventListenerUpdate);
} }
} }
@@ -108,15 +109,6 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) {
instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance);
} }
static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid | .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid |
NfcProtocolFeatureMoreInfo, NfcProtocolFeatureMoreInfo,
@@ -149,7 +141,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
.scene_saved_menu = .scene_saved_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_iso15693_3, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_save_name = .scene_save_name =
{ {
@@ -161,4 +153,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
.on_enter = nfc_scene_emulate_on_enter_iso15693_3, .on_enter = nfc_scene_emulate_on_enter_iso15693_3,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(iso15693_3, NfcProtocolIso15693_3);

View File

@@ -12,10 +12,9 @@
enum { enum {
SubmenuIndexDetectReader = SubmenuIndexCommonMax, SubmenuIndexDetectReader = SubmenuIndexCommonMax,
SubmenuIndexWrite,
SubmenuIndexUpdate,
SubmenuIndexDictAttack, SubmenuIndexDictAttack,
SubmenuIndexCrackNonces, SubmenuIndexCrackNonces,
SubmenuIndexUpdate,
}; };
static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) {
@@ -115,6 +114,9 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) {
Submenu* submenu = instance->submenu; Submenu* submenu = instance->submenu;
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
// Doesn't make sense to show "Write to Initial Card" right after reading
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
if(!mf_classic_is_card_read(data)) { if(!mf_classic_is_card_read(data)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
@@ -160,6 +162,8 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {
Submenu* submenu = instance->submenu; Submenu* submenu = instance->submenu;
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
submenu_change_item_label(submenu, SubmenuIndexCommonWrite, "Write to Initial Card");
if(!mf_classic_is_card_read(data)) { if(!mf_classic_is_card_read(data)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
@@ -175,12 +179,6 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {
nfc_protocol_support_common_submenu_callback, nfc_protocol_support_common_submenu_callback,
instance); instance);
} }
submenu_add_item(
submenu,
"Write to Initial Card",
SubmenuIndexWrite,
nfc_protocol_support_common_submenu_callback,
instance);
submenu_add_item( submenu_add_item(
submenu, submenu,
@@ -215,9 +213,6 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
consumed = true;
} else if(event.event == SubmenuIndexCrackNonces) { } else if(event.event == SubmenuIndexCrackNonces) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces); instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces);
@@ -236,18 +231,15 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneMana
if(event.event == SubmenuIndexDetectReader) { if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial);
consumed = true;
} else if(event.event == SubmenuIndexUpdate) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);
consumed = true;
} else if(event.event == SubmenuIndexDictAttack) { } else if(event.event == SubmenuIndexDictAttack) {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, NfcSceneMfClassicDictAttack)) { instance->scene_manager, NfcSceneMfClassicDictAttack)) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexUpdate) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);
consumed = true;
} }
} }
@@ -267,8 +259,71 @@ static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManag
return consumed; return consumed;
} }
static NfcCommand
nfc_scene_write_poller_callback_mf_classic(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolMfClassic);
NfcApp* instance = context;
MfClassicPollerEvent* mfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
const MfClassicData* write_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
furi_string_set(instance->text_box_store, "Use the source\ncard only");
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
const MfClassicData* tag_data = nfc_poller_get_data(instance->poller);
if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) {
mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite;
} else {
furi_string_set(
instance->text_box_store, "Use source card!\nTo write blanks\nuse NFC Magic app");
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
command = NfcCommandStop;
}
} else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) {
uint8_t sector = mfc_event->data->sec_tr_data.sector_num;
uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector);
if(mf_classic_is_block_read(write_data, sec_tr)) {
mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr];
mfc_event->data->sec_tr_data.sector_trailer_provided = true;
} else {
mfc_event->data->sec_tr_data.sector_trailer_provided = false;
}
} else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) {
uint8_t block_num = mfc_event->data->write_block_data.block_num;
if(mf_classic_is_block_read(write_data, block_num)) {
mfc_event->data->write_block_data.write_block = write_data->block[block_num];
mfc_event->data->write_block_data.write_block_provided = true;
} else {
mfc_event->data->write_block_data.write_block_provided = false;
}
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
} else if(mfc_event->type == MfClassicPollerEventTypeFail) {
furi_string_set(instance->text_box_store, "Not all sectors\nwere written\ncorrectly");
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
}
return command;
}
static void nfc_scene_write_on_enter_mf_classic(NfcApp* instance) {
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_classic, instance);
furi_string_set(instance->text_box_store, "Use the source\ncard only");
}
const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { const NfcProtocolSupportBase nfc_protocol_support_mf_classic = {
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo |
NfcProtocolFeatureWrite,
.scene_info = .scene_info =
{ {
@@ -310,4 +365,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = {
.on_enter = nfc_scene_emulate_on_enter_mf_classic, .on_enter = nfc_scene_emulate_on_enter_mf_classic,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_scene_write_on_enter_mf_classic,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_classic, NfcProtocolMfClassic);

View File

@@ -124,4 +124,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = {
.on_enter = nfc_scene_emulate_on_enter_mf_desfire, .on_enter = nfc_scene_emulate_on_enter_mf_desfire,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_desfire, NfcProtocolMfDesfire);

View File

@@ -25,6 +25,20 @@ static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) {
furi_string_free(temp_str); furi_string_free(temp_str);
} }
static void nfc_scene_more_info_on_enter_mf_plus(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus);
furi_string_reset(instance->text_box_store);
nfc_render_mf_plus_data(data, instance->text_box_store);
text_box_set_font(instance->text_box, TextBoxFontHex);
text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);
}
static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) { static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(event.protocol == NfcProtocolMfPlus); furi_assert(event.protocol == NfcProtocolMfPlus);
@@ -78,7 +92,7 @@ static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) {
} }
const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
.features = NfcProtocolFeatureEmulateUid, .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
.scene_info = .scene_info =
{ {
@@ -87,7 +101,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
}, },
.scene_more_info = .scene_more_info =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_scene_more_info_on_enter_mf_plus,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_read = .scene_read =
@@ -120,4 +134,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
.on_enter = nfc_scene_emulate_on_enter_mf_plus, .on_enter = nfc_scene_emulate_on_enter_mf_plus,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_plus, NfcProtocolMfPlus);

View File

@@ -15,7 +15,21 @@ void nfc_render_mf_plus_info(
} }
void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) { void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) {
nfc_render_mf_plus_version(&data->version, str); MfPlusVersion empty_version = {0};
if(memcmp(&data->version, &empty_version, sizeof(MfPlusVersion)) == 0) {
const char* device_name = mf_plus_get_device_name(data, NfcDeviceNameTypeFull);
if(data->type == MfPlusTypeUnknown || data->size == MfPlusSizeUnknown ||
data->security_level == MfPlusSecurityLevelUnknown) {
furi_string_cat_printf(str, "This %s", device_name);
furi_string_replace(str, " Unknown", "");
} else {
furi_string_cat(str, device_name);
}
furi_string_replace(str, "Mifare", "MIFARE");
furi_string_cat(str, " does not support the GetVersion command, extra info unavailable\n");
} else {
nfc_render_mf_plus_version(&data->version, str);
}
} }
void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) { void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) {

View File

@@ -14,7 +14,6 @@ enum {
SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlock = SubmenuIndexCommonMax,
SubmenuIndexUnlockByReader, SubmenuIndexUnlockByReader,
SubmenuIndexUnlockByPassword, SubmenuIndexUnlockByPassword,
SubmenuIndexWrite,
}; };
enum { enum {
@@ -182,24 +181,22 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
const MfUltralightData* data = const MfUltralightData* data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
bool is_locked = !mf_ultralight_is_all_data_read(data);
if(!mf_ultralight_is_all_data_read(data)) { if(is_locked ||
(data->type != MfUltralightTypeNTAG213 && data->type != MfUltralightTypeNTAG215 &&
data->type != MfUltralightTypeNTAG216 && data->type != MfUltralightTypeUL11 &&
data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin)) {
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
}
if(is_locked) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Unlock", "Unlock",
SubmenuIndexUnlock, SubmenuIndexUnlock,
nfc_protocol_support_common_submenu_callback, nfc_protocol_support_common_submenu_callback,
instance); instance);
} else if(
data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||
data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 ||
data->type == MfUltralightTypeUL21 || data->type == MfUltralightTypeOrigin) {
submenu_add_item(
submenu,
"Write",
SubmenuIndexWrite,
nfc_protocol_support_common_submenu_callback,
instance);
} }
} }
@@ -252,19 +249,57 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
NfcSceneMfUltralightUnlockMenu; NfcSceneMfUltralightUnlockMenu;
scene_manager_next_scene(instance->scene_manager, next_scene); scene_manager_next_scene(instance->scene_manager, next_scene);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
consumed = true;
} else if(event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
consumed = true;
} }
} }
return consumed; return consumed;
} }
static NfcCommand
nfc_scene_write_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolMfUltralight);
NfcApp* instance = context;
MfUltralightPollerEvent* mf_ultralight_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) {
mf_ultralight_event->data->poller_mode = MfUltralightPollerModeWrite;
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
mf_ultralight_event->data->auth_context.skip_auth = true;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestWriteData) {
mf_ultralight_event->data->write_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardMismatch) {
furi_string_set(instance->text_box_store, "Card of the same\ntype should be\n presented");
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardLocked) {
furi_string_set(
instance->text_box_store, "Card protected by\npassword, AUTH0\nor lock bits");
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteFail) {
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteSuccess) {
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
}
return command;
}
static void nfc_scene_write_on_enter_mf_ultralight(NfcApp* instance) {
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_ultralight, instance);
furi_string_set(instance->text_box_store, "Apply the initial\ncard only");
}
const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo |
NfcProtocolFeatureWrite,
.scene_info = .scene_info =
{ {
@@ -306,4 +341,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
.on_enter = nfc_scene_emulate_on_enter_mf_ultralight, .on_enter = nfc_scene_emulate_on_enter_mf_ultralight,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_scene_write_on_enter_mf_ultralight,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_ultralight, NfcProtocolMfUltralight);

View File

@@ -9,9 +9,13 @@
#include "nfc/nfc_app_i.h" #include "nfc/nfc_app_i.h"
#include "nfc_protocol_support_defs.h" #include "nfc_protocol_support_base.h"
#include "nfc_protocol_support_gui_common.h" #include "nfc_protocol_support_gui_common.h"
#include <flipper_application/plugins/plugin_manager.h>
#define TAG "NfcProtocolSupport"
/** /**
* @brief Common scene entry handler. * @brief Common scene entry handler.
* *
@@ -46,6 +50,147 @@ typedef struct {
static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[]; static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[];
const NfcProtocolSupportBase nfc_protocol_support_empty = {
.features = NfcProtocolFeatureNone,
.scene_info =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_more_info =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_success =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};
struct NfcProtocolSupport {
NfcProtocol protocol;
PluginManager* plugin_manager;
const NfcProtocolSupportBase* base;
};
const char* nfc_protocol_support_plugin_names[NfcProtocolNum] = {
[NfcProtocolIso14443_3a] = "iso14443_3a",
[NfcProtocolIso14443_3b] = "iso14443_3b",
[NfcProtocolIso14443_4a] = "iso14443_4a",
[NfcProtocolIso14443_4b] = "iso14443_4b",
[NfcProtocolIso15693_3] = "iso15693_3",
[NfcProtocolFelica] = "felica",
[NfcProtocolMfUltralight] = "mf_ultralight",
[NfcProtocolMfClassic] = "mf_classic",
[NfcProtocolMfPlus] = "mf_plus",
[NfcProtocolMfDesfire] = "mf_desfire",
[NfcProtocolSlix] = "slix",
[NfcProtocolSt25tb] = "st25tb",
[NfcProtocolNtag4xx] = "ntag4xx",
[NfcProtocolType4Tag] = "type_4_tag",
[NfcProtocolEmv] = "emv",
/* Add new protocol support plugin names here */
};
void nfc_protocol_support_alloc(NfcProtocol protocol, void* context) {
furi_assert(context);
NfcApp* instance = context;
NfcProtocolSupport* protocol_support = malloc(sizeof(NfcProtocolSupport));
protocol_support->protocol = protocol;
const char* protocol_name = nfc_protocol_support_plugin_names[protocol];
FuriString* plugin_path =
furi_string_alloc_printf(APP_ASSETS_PATH("plugins/nfc_%s.fal"), protocol_name);
FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(plugin_path));
protocol_support->plugin_manager = plugin_manager_alloc(
NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID,
NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION,
composite_api_resolver_get(instance->api_resolver));
do {
if(plugin_manager_load_single(
protocol_support->plugin_manager, furi_string_get_cstr(plugin_path)) !=
PluginManagerErrorNone) {
break;
}
const NfcProtocolSupportPlugin* plugin =
plugin_manager_get_ep(protocol_support->plugin_manager, 0);
if(plugin->protocol != protocol) {
break;
}
protocol_support->base = plugin->base;
} while(false);
if(!protocol_support->base) {
protocol_support->base = &nfc_protocol_support_empty;
plugin_manager_free(protocol_support->plugin_manager);
protocol_support->plugin_manager = NULL;
}
furi_string_free(plugin_path);
instance->protocol_support = protocol_support;
}
void nfc_protocol_support_free(void* context) {
furi_assert(context);
NfcApp* instance = context;
if(instance->protocol_support->plugin_manager) {
plugin_manager_free(instance->protocol_support->plugin_manager);
}
free(instance->protocol_support);
instance->protocol_support = NULL;
}
static const NfcProtocolSupportBase*
nfc_protocol_support_get(NfcProtocol protocol, void* context) {
furi_assert(context);
NfcApp* instance = context;
if(instance->protocol_support && instance->protocol_support->protocol != protocol) {
nfc_protocol_support_free(instance);
}
if(!instance->protocol_support) {
nfc_protocol_support_alloc(protocol, instance);
}
return instance->protocol_support->base;
}
// Interface functions // Interface functions
void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) { void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) {
furi_assert(scene < NfcProtocolSupportSceneCount); furi_assert(scene < NfcProtocolSupportSceneCount);
@@ -74,17 +219,23 @@ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context)
nfc_protocol_support_scenes[scene].on_exit(instance); nfc_protocol_support_scenes[scene].on_exit(instance);
} }
bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) { bool nfc_protocol_support_has_feature(
return nfc_protocol_support[protocol]->features & feature; NfcProtocol protocol,
void* context,
NfcProtocolFeature feature) {
furi_assert(context);
NfcApp* instance = context;
return nfc_protocol_support_get(protocol, instance)->features & feature;
} }
// Common scene handlers // Common scene handlers
// SceneInfo // SceneInfo
static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) { static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) {
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
nfc_protocol_support[protocol]->scene_info.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_info.on_enter(instance);
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureMoreInfo)) { if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureMoreInfo)) {
widget_add_button_element( widget_add_button_element(
instance->widget, instance->widget,
GuiButtonTypeRight, GuiButtonTypeRight,
@@ -124,7 +275,7 @@ static void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) {
// SceneMoreInfo // SceneMoreInfo
static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) { static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) {
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
nfc_protocol_support[protocol]->scene_more_info.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_more_info.on_enter(instance);
} }
static bool static bool
@@ -132,7 +283,8 @@ static bool
bool consumed = false; bool consumed = false;
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event); consumed =
nfc_protocol_support_get(protocol, instance)->scene_more_info.on_event(instance, event);
return consumed; return consumed;
} }
@@ -157,7 +309,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
//nfc_supported_cards_load_cache(instance->nfc_supported_cards); //nfc_supported_cards_load_cache(instance->nfc_supported_cards);
// Start poller with the appropriate callback // Start poller with the appropriate callback
nfc_protocol_support[protocol]->scene_read.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_read.on_enter(instance);
nfc_blink_read_start(instance); nfc_blink_read_start(instance);
} }
@@ -186,7 +338,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
} else { } else {
const NfcProtocol protocol = const NfcProtocol protocol =
nfc_detected_protocols_get_selected(instance->detected_protocols); nfc_detected_protocols_get_selected(instance->detected_protocols);
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); consumed = nfc_protocol_support_get(protocol, instance)
->scene_read.on_event(instance, event);
} }
} else if(event.event == NfcCustomEventPollerFailure) { } else if(event.event == NfcCustomEventPollerFailure) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
@@ -199,7 +352,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
} else if(event.event == NfcCustomEventCardDetected) { } else if(event.event == NfcCustomEventCardDetected) {
const NfcProtocol protocol = const NfcProtocol protocol =
nfc_detected_protocols_get_selected(instance->detected_protocols); nfc_detected_protocols_get_selected(instance->detected_protocols);
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); consumed =
nfc_protocol_support_get(protocol, instance)->scene_read.on_event(instance, event);
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
@@ -241,7 +395,7 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {
instance); instance);
} }
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Emulate UID", "Emulate UID",
@@ -249,7 +403,7 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {
nfc_protocol_support_common_submenu_callback, nfc_protocol_support_common_submenu_callback,
instance); instance);
} else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { } else if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateFull)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Emulate", "Emulate",
@@ -258,7 +412,16 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {
instance); instance);
} }
nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance); if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureWrite)) {
submenu_add_item(
submenu,
"Write",
SubmenuIndexCommonWrite,
nfc_protocol_support_common_submenu_callback,
instance);
}
nfc_protocol_support_get(protocol, instance)->scene_read_menu.on_enter(instance);
submenu_add_item( submenu_add_item(
submenu, submenu,
@@ -291,9 +454,17 @@ static bool
dolphin_deed(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexCommonWrite) {
dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
consumed = true;
} else if(event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
consumed = true;
} else { } else {
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); consumed = nfc_protocol_support_get(protocol, instance)
->scene_read_menu.on_event(instance, event);
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
@@ -312,13 +483,17 @@ static void nfc_protocol_support_scene_read_saved_menu_on_exit(NfcApp* instance)
static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) { static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {
Widget* widget = instance->widget; Widget* widget = instance->widget;
popup_set_header(instance->popup, "Parsing", 85, 27, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 12, 23, &A_Loading_24);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();
if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) { if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
widget_add_text_scroll_element( widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
} else { } else {
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
nfc_protocol_support[protocol]->scene_read_success.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_read_success.on_enter(instance);
} }
furi_string_free(temp_str); furi_string_free(temp_str);
@@ -366,7 +541,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
Submenu* submenu = instance->submenu; Submenu* submenu = instance->submenu;
// Header submenu items // Header submenu items
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Emulate UID", "Emulate UID",
@@ -374,7 +549,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
nfc_protocol_support_common_submenu_callback, nfc_protocol_support_common_submenu_callback,
instance); instance);
} else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { } else if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateFull)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Emulate", "Emulate",
@@ -383,7 +558,16 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
instance); instance);
} }
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) { if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureWrite)) {
submenu_add_item(
submenu,
"Write",
SubmenuIndexCommonWrite,
nfc_protocol_support_common_submenu_callback,
instance);
}
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEditUid)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Edit UID", "Edit UID",
@@ -393,7 +577,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
} }
// Protocol-dependent menu items // Protocol-dependent menu items
nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_saved_menu.on_enter(instance);
// Trailer submenu items // Trailer submenu items
if(nfc_has_shadow_file(instance)) { if(nfc_has_shadow_file(instance)) {
@@ -456,12 +640,19 @@ static bool
dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate);
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexCommonWrite) {
const bool is_added =
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType);
dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate);
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
consumed = true;
} else if(event.event == SubmenuIndexCommonEdit) { } else if(event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
consumed = true; consumed = true;
} else { } else {
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event); consumed = nfc_protocol_support_get(protocol, instance)
->scene_saved_menu.on_event(instance, event);
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
@@ -480,8 +671,18 @@ static void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) {
bool name_is_empty = furi_string_empty(instance->file_name); bool name_is_empty = furi_string_empty(instance->file_name);
if(name_is_empty) { if(name_is_empty) {
furi_string_set(instance->file_path, NFC_APP_FOLDER); furi_string_set(instance->file_path, NFC_APP_FOLDER);
name_generator_make_auto_basic( FuriString* prefix = furi_string_alloc();
instance->text_store, NFC_TEXT_STORE_SIZE, NFC_APP_FILENAME_PREFIX); furi_string_set(prefix, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull));
furi_string_replace(prefix, "Mifare", "MF");
furi_string_replace(prefix, " Classic", "C"); // MFC
furi_string_replace(prefix, "Desfire", "Des"); // MF Des
furi_string_replace(prefix, "Ultralight", "UL"); // MF UL
furi_string_replace(prefix, " Plus", "+"); // NTAG I2C+
furi_string_replace(prefix, " (Unknown)", "");
furi_string_replace_all(prefix, " ", "_");
name_generator_make_auto(
instance->text_store, NFC_TEXT_STORE_SIZE, furi_string_get_cstr(prefix));
furi_string_free(prefix);
furi_string_set(folder_path, NFC_APP_FOLDER); furi_string_set(folder_path, NFC_APP_FOLDER);
} else { } else {
nfc_text_store_set(instance, "%s", furi_string_get_cstr(instance->file_name)); nfc_text_store_set(instance, "%s", furi_string_get_cstr(instance->file_name));
@@ -527,8 +728,8 @@ static bool
DolphinDeedNfcSave); DolphinDeedNfcSave);
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
consumed = consumed = nfc_protocol_support_get(protocol, instance)
nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event); ->scene_save_name.on_event(instance, event);
} else { } else {
consumed = scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, NfcSceneStart); instance->scene_manager, NfcSceneStart);
@@ -570,9 +771,9 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64); widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64);
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) {
widget_add_string_element( widget_add_string_element(
widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID"); widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID");
@@ -613,7 +814,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
furi_string_reset(instance->text_box_store); furi_string_reset(instance->text_box_store);
// instance->listener is allocated in the respective on_enter() handler // instance->listener is allocated in the respective on_enter() handler
nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_emulate.on_enter(instance);
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget); instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget);
@@ -692,6 +893,191 @@ static void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) {
nfc_blink_stop(instance); nfc_blink_stop(instance);
} }
// SceneWrite
/**
* @brief Current view displayed on the write scene.
*
* The emulation scene has five states, some protocols may not use all states.
* Protocol handles poller events, when scene state needs to change it should
* fill text_box_store with a short caption (when applicable) before sending
* the relevant view dispatcher event.
*/
enum {
NfcSceneWriteStateSearching, /**< Ask user to touch the card. Event: on_enter, CardLost. Needs caption. */
NfcSceneWriteStateWriting, /**< Ask not to move while writing. Event: CardDetected. No caption. */
NfcSceneWriteStateSuccess, /**< Card written successfully. Event: PollerSuccess. No caption. */
NfcSceneWriteStateFailure, /**< An error is displayed. Event: PollerFailure. Needs caption. */
NfcSceneWriteStateWrongCard, /**< Wrong card was presented. Event: WrongCard. Needs caption. */
};
static void nfc_protocol_support_scene_write_popup_callback(void* context) {
NfcApp* instance = context;
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_protocol_support_scene_write_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
NfcApp* instance = context;
if(type == InputTypeShort && result == GuiButtonTypeLeft) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventRetry);
}
}
static void nfc_protocol_support_scene_write_setup_view(NfcApp* instance) {
Popup* popup = instance->popup;
Widget* widget = instance->widget;
popup_reset(popup);
widget_reset(widget);
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneWrite);
NfcView view = NfcViewPopup;
if(state == NfcSceneWriteStateSearching) {
popup_set_header(popup, "Writing", 95, 20, AlignCenter, AlignCenter);
popup_set_text(
popup,
furi_string_get_cstr(instance->text_box_store),
95,
38,
AlignCenter,
AlignCenter);
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
} else if(state == NfcSceneWriteStateWriting) {
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
popup_set_icon(popup, 12, 23, &A_Loading_24);
} else if(state == NfcSceneWriteStateSuccess) {
popup_set_header(popup, "Successfully\nwritten!", 126, 2, AlignRight, AlignTop);
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_timeout(popup, 1500);
popup_set_context(popup, instance);
popup_set_callback(popup, nfc_protocol_support_scene_write_popup_callback);
popup_enable_timeout(popup);
} else if(state == NfcSceneWriteStateFailure) {
view = NfcViewWidget;
widget_add_string_element(
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
widget_add_string_multiline_element(
widget,
7,
17,
AlignLeft,
AlignTop,
FontSecondary,
furi_string_get_cstr(instance->text_box_store));
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Retry",
nfc_protocol_support_scene_write_widget_callback,
instance);
} else if(state == NfcSceneWriteStateWrongCard) {
view = NfcViewWidget;
widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wrong card!");
widget_add_string_multiline_element(
widget,
4,
17,
AlignLeft,
AlignTop,
FontSecondary,
furi_string_get_cstr(instance->text_box_store));
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Retry",
nfc_protocol_support_scene_write_widget_callback,
instance);
}
view_dispatcher_switch_to_view(instance->view_dispatcher, view);
}
static void nfc_protocol_support_scene_write_on_enter(NfcApp* instance) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneWrite, NfcSceneWriteStateSearching);
furi_string_reset(instance->text_box_store);
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
// instance->poller is allocated in the respective on_enter() handler
nfc_protocol_support_get(protocol, instance)->scene_write.on_enter(instance);
nfc_protocol_support_scene_write_setup_view(instance);
nfc_blink_emulate_start(instance);
}
static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
uint32_t new_state = -1;
bool stop_poller = false;
if(event.event == NfcCustomEventCardDetected) {
new_state = NfcSceneWriteStateWriting;
consumed = true;
} else if(event.event == NfcCustomEventCardLost) {
new_state = NfcSceneWriteStateSearching;
consumed = true;
} else if(event.event == NfcCustomEventPollerSuccess) {
dolphin_deed(DolphinDeedNfcSave);
notification_message(instance->notifications, &sequence_success);
new_state = NfcSceneWriteStateSuccess;
stop_poller = true;
consumed = true;
} else if(event.event == NfcCustomEventPollerFailure) {
notification_message(instance->notifications, &sequence_error);
new_state = NfcSceneWriteStateFailure;
stop_poller = true;
consumed = true;
} else if(event.event == NfcCustomEventWrongCard) {
notification_message(instance->notifications, &sequence_error);
new_state = NfcSceneWriteStateWrongCard;
stop_poller = true;
consumed = true;
} else if(event.event == NfcCustomEventViewExit) {
scene_manager_previous_scene(instance->scene_manager);
consumed = true;
} else if(event.event == NfcCustomEventRetry) {
nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_exit(instance);
nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_enter(instance);
consumed = true;
}
if(stop_poller) {
if(instance->poller) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
instance->poller = NULL;
}
nfc_blink_stop(instance);
}
if(new_state != (uint32_t)-1) {
scene_manager_set_scene_state(instance->scene_manager, NfcSceneWrite, new_state);
nfc_protocol_support_scene_write_setup_view(instance);
}
}
return consumed;
}
static void nfc_protocol_support_scene_write_on_exit(NfcApp* instance) {
if(instance->poller) {
nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller);
}
// Clear view
popup_reset(instance->popup);
widget_reset(instance->widget);
furi_string_reset(instance->text_box_store);
nfc_blink_stop(instance);
}
static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) { static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) {
UNUSED(instance); UNUSED(instance);
} }
@@ -709,7 +1095,7 @@ static void nfc_protocol_support_scene_rpc_setup_ui_and_emulate(NfcApp* instance
nfc_blink_emulate_start(instance); nfc_blink_emulate_start(instance);
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); nfc_protocol_support_get(protocol, instance)->scene_emulate.on_enter(instance);
instance->rpc_state = NfcRpcStateEmulating; instance->rpc_state = NfcRpcStateEmulating;
} }
@@ -807,6 +1193,12 @@ static const NfcProtocolSupportCommonSceneBase
.on_event = nfc_protocol_support_scene_emulate_on_event, .on_event = nfc_protocol_support_scene_emulate_on_event,
.on_exit = nfc_protocol_support_scene_emulate_on_exit, .on_exit = nfc_protocol_support_scene_emulate_on_exit,
}, },
[NfcProtocolSupportSceneWrite] =
{
.on_enter = nfc_protocol_support_scene_write_on_enter,
.on_event = nfc_protocol_support_scene_write_on_event,
.on_exit = nfc_protocol_support_scene_write_on_exit,
},
[NfcProtocolSupportSceneRpc] = [NfcProtocolSupportSceneRpc] =
{ {
.on_enter = nfc_protocol_support_scene_rpc_on_enter, .on_enter = nfc_protocol_support_scene_rpc_on_enter,

View File

@@ -40,7 +40,7 @@
* *
* | Filename | Explanation | * | Filename | Explanation |
* |:-----------------------|:------------| * |:-----------------------|:------------|
* | protocol_name.h | Interface structure declaration used in `nfc_protocol_support_defs.c`. | * | protocol_name.h | Interface structure declaration. |
* | protocol_name.c | Protocol-specific scene implemenatations and definitions. | * | protocol_name.c | Protocol-specific scene implemenatations and definitions. |
* | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. | * | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. |
* | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.| * | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.|
@@ -65,8 +65,13 @@
* *
* After completing the protocol support, it must be registered within the application in order for it to be usable. * After completing the protocol support, it must be registered within the application in order for it to be usable.
* *
* In nfc_protocol_support_defs.c, include the `protocol_name.h` file and add a new entry in the `nfc_protocol_support[]` * In `protocol_name.c`, add `NFC_PROTOCOL_SUPPORT_PLUGIN(protocol_name, NfcProtocolName)` at the bottom,
* array under the appropriate index. * below the `NfcProtocolSupportBase` structure definition.
*
* In `application.fam`, add a new entry for the plugin, following the other examples.
*
* In nfc_protocol_support.c, add a new entry in the `nfc_protocol_support_plugin_names[]`
* array under the appropriate index with the name of the plugin (without the `nfc_` prefix).
* *
* ## Done! * ## Done!
* *
@@ -80,6 +85,10 @@
#include "nfc_protocol_support_common.h" #include "nfc_protocol_support_common.h"
typedef struct NfcProtocolSupport NfcProtocolSupport;
void nfc_protocol_support_free(void* context);
/** /**
* @brief Abstract interface for on_enter() scene handler. * @brief Abstract interface for on_enter() scene handler.
* *
@@ -113,4 +122,7 @@ bool nfc_protocol_support_on_event(
*/ */
void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context); void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context);
bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature); bool nfc_protocol_support_has_feature(
NfcProtocol protocol,
void* context,
NfcProtocolFeature feature);

View File

@@ -9,6 +9,8 @@
#include "../../nfc_app.h" #include "../../nfc_app.h"
#include "../../nfc_app_i.h" #include "../../nfc_app_i.h"
#include <lib/flipper_application/flipper_application.h>
/** /**
* @brief Scene entry handler. * @brief Scene entry handler.
* *
@@ -114,4 +116,47 @@ typedef struct {
* It is responsible for creating a listener and for handling its events. * It is responsible for creating a listener and for handling its events.
*/ */
NfcProtocolSupportSceneBase scene_emulate; NfcProtocolSupportSceneBase scene_emulate;
/**
* @brief Handlers for protocol-specific write scene.
*
* This scene is activated when a write operation is in progress.
* It is responsible for creating a poller, handling its events and
* displaying short captions for what is happening.
*/
NfcProtocolSupportSceneBase scene_write;
} NfcProtocolSupportBase; } NfcProtocolSupportBase;
/**
* @brief Unique string identifier for protocol support plugins.
*/
#define NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID "NfcProtocolSupportPlugin"
/**
* @brief Currently supported plugin API version.
*/
#define NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION 1
/**
* @brief Protocol support plugin interface.
*/
typedef struct {
NfcProtocol protocol; /**< Identifier of the protocol this plugin implements. */
const NfcProtocolSupportBase* base; /**< Pointer to the protocol support interface. */
} NfcProtocolSupportPlugin;
#define NFC_PROTOCOL_SUPPORT_PLUGIN(name, protocol) \
static const NfcProtocolSupportPlugin nfc_protocol_support_##name##_desc = { \
protocol, \
&nfc_protocol_support_##name, \
}; \
\
static const FlipperAppPluginDescriptor plugin_descriptor_##name = { \
.appid = NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID, \
.ep_api_version = NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION, \
.entry_point = &nfc_protocol_support_##name##_desc, \
}; \
\
const FlipperAppPluginDescriptor* nfc_##name##_ep(void) { \
return &plugin_descriptor_##name; \
}

View File

@@ -13,6 +13,7 @@ typedef enum {
NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */ NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */
NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */ NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */
NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */ NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */
NfcProtocolFeatureWrite = 1UL << 4, /**< Writing to real card is supported. */
} NfcProtocolFeature; } NfcProtocolFeature;
/** /**
@@ -30,6 +31,7 @@ typedef enum {
NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */ NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */
NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */ NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */
NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */ NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */
NfcProtocolSupportSceneWrite, /**< Shown when writing to a card. */
NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */ NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */
NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */ NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */

View File

@@ -1,49 +0,0 @@
/**
* @file nfc_protocol_support_defs.c
* @brief Application-level protocol support definitions.
*
* This file is to be modified whenever support for
* a new protocol is to be added.
*/
#include "nfc_protocol_support_base.h"
#include <nfc/protocols/nfc_protocol.h>
#include "iso14443_3a/iso14443_3a.h"
#include "iso14443_3b/iso14443_3b.h"
#include "iso14443_4a/iso14443_4a.h"
#include "iso14443_4b/iso14443_4b.h"
#include "iso15693_3/iso15693_3.h"
#include "felica/felica.h"
#include "mf_ultralight/mf_ultralight.h"
#include "mf_classic/mf_classic.h"
#include "mf_plus/mf_plus.h"
#include "mf_desfire/mf_desfire.h"
#include "emv/emv.h"
#include "slix/slix.h"
#include "st25tb/st25tb.h"
/**
* @brief Array of pointers to concrete protocol support implementations.
*
* When adding support for a new protocol, add it to the end of this array
* under its respective index.
*
* @see nfc_protocol.h
*/
const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = {
[NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a,
[NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b,
[NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a,
[NfcProtocolIso14443_4b] = &nfc_protocol_support_iso14443_4b,
[NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3,
[NfcProtocolFelica] = &nfc_protocol_support_felica,
[NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight,
[NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic,
[NfcProtocolMfPlus] = &nfc_protocol_support_mf_plus,
[NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire,
[NfcProtocolSlix] = &nfc_protocol_support_slix,
[NfcProtocolSt25tb] = &nfc_protocol_support_st25tb,
[NfcProtocolEmv] = &nfc_protocol_support_emv,
/* Add new protocol support implementations here */
};

View File

@@ -1,12 +0,0 @@
/**
* @file nfc_protocol_support_defs.h
* @brief Application-level protocol support declarations.
*/
#pragma once
#include "nfc_protocol_support_base.h"
/**
* @brief Declaraion of array of pointers to protocol support implementations.
*/
extern const NfcProtocolSupportBase* nfc_protocol_support[];

View File

@@ -26,9 +26,9 @@ void nfc_protocol_support_common_byte_input_done_callback(void* context) {
} }
void nfc_protocol_support_common_text_input_done_callback(void* context) { void nfc_protocol_support_common_text_input_done_callback(void* context) {
NfcApp* nfc = context; NfcApp* instance = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTextInputDone);
} }
void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) {

View File

@@ -15,6 +15,7 @@
enum { enum {
SubmenuIndexCommonSave, /**< Save menu option. */ SubmenuIndexCommonSave, /**< Save menu option. */
SubmenuIndexCommonEmulate, /**< Emulate menu option. */ SubmenuIndexCommonEmulate, /**< Emulate menu option. */
SubmenuIndexCommonWrite, /**< Write menu option. */
SubmenuIndexCommonEdit, /**< Edit menu option. */ SubmenuIndexCommonEdit, /**< Edit menu option. */
SubmenuIndexCommonInfo, /**< Info menu option. */ SubmenuIndexCommonInfo, /**< Info menu option. */
SubmenuIndexCommonRename, /**< Rename menu option. */ SubmenuIndexCommonRename, /**< Rename menu option. */
@@ -23,6 +24,10 @@ enum {
SubmenuIndexCommonMax, /**< Special value, internal use. */ SubmenuIndexCommonMax, /**< Special value, internal use. */
}; };
#ifdef __cplusplus
extern "C" {
#endif
/** /**
* @brief Common submenu callback. * @brief Common submenu callback.
* *
@@ -84,3 +89,7 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance);
* @returns always true. * @returns always true.
*/ */
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event); bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event);
#ifdef __cplusplus
}
#endif

View File

@@ -5,5 +5,13 @@ typedef enum {
NfcSceneReadMenuStateCardFound, NfcSceneReadMenuStateCardFound,
} NfcSceneUnlockReadState; } NfcSceneUnlockReadState;
#ifdef __cplusplus
extern "C" {
#endif
void nfc_unlock_helper_setup_from_state(NfcApp* instance); void nfc_unlock_helper_setup_from_state(NfcApp* instance);
void nfc_unlock_helper_card_detected_handler(NfcApp* instance); void nfc_unlock_helper_card_detected_handler(NfcApp* instance);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,140 @@
#include "ntag4xx.h"
#include "ntag4xx_render.h"
#include <nfc/protocols/ntag4xx/ntag4xx_poller.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
#include "../iso14443_4a/iso14443_4a_i.h"
static void nfc_scene_info_on_enter_ntag4xx(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx);
FuriString* temp_str = furi_string_alloc();
nfc_append_filename_string_when_present(instance, temp_str);
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeFull, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static void nfc_scene_more_info_on_enter_ntag4xx(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx);
furi_string_reset(instance->text_box_store);
nfc_render_ntag4xx_data(data, instance->text_box_store);
text_box_set_font(instance->text_box, TextBoxFontHex);
text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);
}
static NfcCommand nfc_scene_read_poller_callback_ntag4xx(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolNtag4xx);
NfcCommand command = NfcCommandContinue;
NfcApp* instance = context;
const Ntag4xxPollerEvent* ntag4xx_event = event.event_data;
if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolNtag4xx, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
} else if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadFailed) {
command = NfcCommandReset;
}
return command;
}
static void nfc_scene_read_on_enter_ntag4xx(NfcApp* instance) {
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_ntag4xx, instance);
}
static void nfc_scene_read_success_on_enter_ntag4xx(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeShort, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static void nfc_scene_emulate_on_enter_ntag4xx(NfcApp* instance) {
const Iso14443_4aData* iso14443_4a_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
instance->listener =
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
nfc_listener_start(
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
}
const NfcProtocolSupportBase nfc_protocol_support_ntag4xx = {
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
.scene_info =
{
.on_enter = nfc_scene_info_on_enter_ntag4xx,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_more_info =
{
.on_enter = nfc_scene_more_info_on_enter_ntag4xx,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_ntag4xx,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_success =
{
.on_enter = nfc_scene_read_success_on_enter_ntag4xx,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_scene_emulate_on_enter_ntag4xx,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};
NFC_PROTOCOL_SUPPORT_PLUGIN(ntag4xx, NfcProtocolNtag4xx);

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_protocol_support_base.h"
extern const NfcProtocolSupportBase nfc_protocol_support_ntag4xx;

View File

@@ -0,0 +1,110 @@
#include "ntag4xx_render.h"
#include "../iso14443_4a/iso14443_4a_render.h"
void nfc_render_ntag4xx_info(
const Ntag4xxData* data,
NfcProtocolFormatType format_type,
FuriString* str) {
nfc_render_iso14443_4a_brief(ntag4xx_get_base_data(data), str);
const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version);
if(type >= Ntag4xxTypeUnknown) {
furi_string_cat(str, "Memory Size: unknown");
} else {
size_t size_cc = 32;
size_t size_ndef = 0;
size_t size_proprietary = 0;
bool has_tagtamper = false;
switch(type) {
case Ntag4xxType413DNA:
size_ndef = 128;
size_proprietary = 0;
break;
case Ntag4xxType424DNATT:
has_tagtamper = true;
/* fall-through */
case Ntag4xxType424DNA:
size_ndef = 256;
size_proprietary = 128;
break;
case Ntag4xxType426QDNATT:
has_tagtamper = true;
/* fall-through */
case Ntag4xxType426QDNA:
size_ndef = 768;
size_proprietary = 128;
break;
default:
break;
}
furi_string_cat_printf(
str, "\nMemory Size: %zu bytes\n", size_cc + size_ndef + size_proprietary);
furi_string_cat_printf(str, "Usable NDEF Size: %zu bytes\n", size_ndef - sizeof(uint16_t));
furi_string_cat_printf(str, "Capability Cont.: %zu bytes\n", size_cc);
if(size_proprietary) {
furi_string_cat_printf(str, "Proprietary File: %zu bytes\n", size_proprietary);
}
furi_string_cat_printf(str, "TagTamper: %ssupported", has_tagtamper ? "" : "not ");
}
if(format_type != NfcProtocolFormatTypeFull) return;
furi_string_cat(str, "\n\e#ISO14443-4 data");
nfc_render_iso14443_4a_extra(ntag4xx_get_base_data(data), str);
}
void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str) {
nfc_render_ntag4xx_version(&data->version, str);
}
void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str) {
furi_string_cat_printf(
str,
"%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
data->uid[0],
data->uid[1],
data->uid[2],
data->uid[3],
data->uid[4],
data->uid[5],
data->uid[6]);
furi_string_cat_printf(
str,
"hw %02x type %02x sub %02x\n"
" maj %02x min %02x\n"
" size %02x proto %02x\n",
data->hw_vendor,
data->hw_type,
data->hw_subtype,
data->hw_major,
data->hw_minor,
data->hw_storage,
data->hw_proto);
furi_string_cat_printf(
str,
"sw %02x type %02x sub %02x\n"
" maj %02x min %02x\n"
" size %02x proto %02x\n",
data->sw_vendor,
data->sw_type,
data->sw_subtype,
data->sw_major,
data->sw_minor,
data->sw_storage,
data->sw_proto);
furi_string_cat_printf(
str,
"batch %02x:%02x:%02x:%02x:%01x\n"
"week %d year %d\n"
"fab key %02x id %02x\n",
data->batch[0],
data->batch[1],
data->batch[2],
data->batch[3],
data->batch_extra,
data->prod_week,
data->prod_year,
(data->fab_key_4b << 1) | (data->fab_key_1b),
data->optional.fab_key_id);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <nfc/protocols/ntag4xx/ntag4xx.h>
#include "../nfc_protocol_support_render_common.h"
void nfc_render_ntag4xx_info(
const Ntag4xxData* data,
NfcProtocolFormatType format_type,
FuriString* str);
void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str);
void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str);

View File

@@ -78,20 +78,21 @@ static NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event
furi_assert(event.protocol == NfcProtocolSlix); furi_assert(event.protocol == NfcProtocolSlix);
furi_assert(event.event_data); furi_assert(event.event_data);
NfcApp* nfc = context; NfcApp* instance = context;
SlixListenerEvent* slix_event = event.event_data; SlixListenerEvent* slix_event = event.event_data;
if(slix_event->type == SlixListenerEventTypeCustomCommand) { if(slix_event->type == SlixListenerEventTypeCustomCommand) {
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(nfc->text_box_store, "R:"); furi_string_cat_printf(instance->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) { for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) {
furi_string_cat_printf( furi_string_cat_printf(
nfc->text_box_store, instance->text_box_store,
" %02X", " %02X",
bit_buffer_get_byte(slix_event->data->buffer, i)); bit_buffer_get_byte(slix_event->data->buffer, i));
} }
furi_string_push_back(nfc->text_box_store, '\n'); furi_string_push_back(instance->text_box_store, '\n');
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventListenerUpdate);
} }
} }
@@ -105,15 +106,6 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) {
nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance);
} }
static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_slix = { const NfcProtocolSupportBase nfc_protocol_support_slix = {
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
@@ -145,7 +137,7 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = {
.scene_saved_menu = .scene_saved_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_slix, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_save_name = .scene_save_name =
{ {
@@ -157,4 +149,11 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = {
.on_enter = nfc_scene_emulate_on_enter_slix, .on_enter = nfc_scene_emulate_on_enter_slix,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(slix, NfcProtocolSlix);

View File

@@ -61,15 +61,6 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) {
furi_string_free(temp_str); furi_string_free(temp_str);
} }
static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_st25tb = { const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
.features = NfcProtocolFeatureNone, .features = NfcProtocolFeatureNone,
@@ -96,7 +87,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
.scene_saved_menu = .scene_saved_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_st25tb, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_save_name = .scene_save_name =
{ {
@@ -108,4 +99,11 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_write =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
}; };
NFC_PROTOCOL_SUPPORT_PLUGIN(st25tb, NfcProtocolSt25tb);

View File

@@ -0,0 +1,260 @@
#include "type_4_tag.h"
#include "type_4_tag_render.h"
#include <nfc/protocols/type_4_tag/type_4_tag_poller.h>
#include <nfc/protocols/type_4_tag/type_4_tag_listener.h>
#include <toolbox/pretty_format.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
enum {
NfcSceneMoreInfoStateASCII,
NfcSceneMoreInfoStateRawData,
};
static void nfc_scene_info_on_enter_type_4_tag(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
FuriString* temp_str = furi_string_alloc();
nfc_append_filename_string_when_present(instance, temp_str);
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeFull, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static void nfc_scene_more_info_on_enter_type_4_tag(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
furi_string_reset(instance->text_box_store);
uint32_t scene_state =
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo);
if(scene_state == NfcSceneMoreInfoStateASCII) {
if(simple_array_get_count(data->ndef_data) == 0) {
furi_string_cat_str(instance->text_box_store, "No NDEF data to show");
} else {
pretty_format_bytes_hex_canonical(
instance->text_box_store,
TYPE_4_TAG_RENDER_BYTES_PER_LINE,
PRETTY_FORMAT_FONT_MONOSPACE,
simple_array_cget_data(data->ndef_data),
simple_array_get_count(data->ndef_data));
}
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
widget_add_button_element(
instance->widget,
GuiButtonTypeRight,
"Raw Data",
nfc_protocol_support_common_widget_callback,
instance);
widget_add_button_element(
instance->widget,
GuiButtonTypeLeft,
"Info",
nfc_protocol_support_common_widget_callback,
instance);
} else if(scene_state == NfcSceneMoreInfoStateRawData) {
nfc_render_type_4_tag_dump(data, instance->text_box_store);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
widget_add_button_element(
instance->widget,
GuiButtonTypeLeft,
"ASCII",
nfc_protocol_support_common_widget_callback,
instance);
}
}
static bool nfc_scene_more_info_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) {
bool consumed = false;
if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) ||
(event.type == SceneManagerEventTypeBack)) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII);
scene_manager_previous_scene(instance->scene_manager);
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData);
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
consumed = true;
}
return consumed;
}
static NfcCommand nfc_scene_read_poller_callback_type_4_tag(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolType4Tag);
NfcCommand command = NfcCommandContinue;
NfcApp* instance = context;
const Type4TagPollerEvent* type_4_tag_event = event.event_data;
if(type_4_tag_event->type == Type4TagPollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolType4Tag, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
} else if(type_4_tag_event->type == Type4TagPollerEventTypeReadFailed) {
command = NfcCommandReset;
}
return command;
}
static void nfc_scene_read_on_enter_type_4_tag(NfcApp* instance) {
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_type_4_tag, instance);
}
static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeShort, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static NfcCommand
nfc_scene_emulate_listener_callback_type_4_tag(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolType4Tag);
NfcApp* instance = context;
Type4TagListenerEvent* type_4_tag_event = event.event_data;
if(type_4_tag_event->type == Type4TagListenerEventTypeCustomCommand) {
if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(instance->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(type_4_tag_event->data->buffer); i++) {
furi_string_cat_printf(
instance->text_box_store,
" %02X",
bit_buffer_get_byte(type_4_tag_event->data->buffer, i));
}
furi_string_push_back(instance->text_box_store, '\n');
view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventListenerUpdate);
}
}
return NfcCommandContinue;
}
static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) {
const Type4TagData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag);
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolType4Tag, data);
nfc_listener_start(
instance->listener, nfc_scene_emulate_listener_callback_type_4_tag, instance);
}
static NfcCommand
nfc_scene_write_poller_callback_type_4_tag(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolType4Tag);
NfcApp* instance = context;
Type4TagPollerEvent* type_4_tag_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(type_4_tag_event->type == Type4TagPollerEventTypeRequestMode) {
type_4_tag_event->data->poller_mode.mode = Type4TagPollerModeWrite;
type_4_tag_event->data->poller_mode.data =
nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag);
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteFail) {
const char* error_str = type_4_tag_event->data->error == Type4TagErrorCardLocked ?
"Card does not\nallow writing\nnew data" :
"Failed to\nwrite new data";
furi_string_set(instance->text_box_store, error_str);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteSuccess) {
furi_string_reset(instance->text_box_store);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
}
return command;
}
static void nfc_scene_write_on_enter_type_4_tag(NfcApp* instance) {
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolType4Tag);
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_type_4_tag, instance);
furi_string_set(instance->text_box_store, "Apply card\nto the back");
}
const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = {
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo |
NfcProtocolFeatureWrite,
.scene_info =
{
.on_enter = nfc_scene_info_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_more_info =
{
.on_enter = nfc_scene_more_info_on_enter_type_4_tag,
.on_event = nfc_scene_more_info_on_event_type_4_tag,
},
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_success =
{
.on_enter = nfc_scene_read_success_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_scene_emulate_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_write =
{
.on_enter = nfc_scene_write_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};
NFC_PROTOCOL_SUPPORT_PLUGIN(type_4_tag, NfcProtocolType4Tag);

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_protocol_support_base.h"
extern const NfcProtocolSupportBase nfc_protocol_support_type_4_tag;

View File

@@ -0,0 +1,59 @@
#include "type_4_tag_render.h"
#include "../iso14443_4a/iso14443_4a_render.h"
void nfc_render_type_4_tag_info(
const Type4TagData* data,
NfcProtocolFormatType format_type,
FuriString* str) {
nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str);
furi_string_cat(str, "\n:::::::::::::::[Stored NDEF]:::::::::::::::\n");
furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data));
if(data->is_tag_specific) {
furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n");
furi_string_cat_printf(
str,
"Card: %s\n",
furi_string_empty(data->platform_name) ? "unknown" :
furi_string_get_cstr(data->platform_name));
furi_string_cat_printf(
str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor);
furi_string_cat_printf(str, "NDEF File ID: %04X\n", data->ndef_file_id);
furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len);
furi_string_cat_printf(
str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write);
furi_string_cat_printf(
str,
"Read Lock: %02X%s\n",
data->ndef_read_lock,
data->ndef_read_lock == 0 ? " (unlocked)" : "");
furi_string_cat_printf(
str,
"Write Lock: %02X%s",
data->ndef_write_lock,
data->ndef_write_lock == 0 ? " (unlocked)" : "");
}
if(format_type != NfcProtocolFormatTypeFull) return;
furi_string_cat(str, "\n\e#ISO14443-4 data");
nfc_render_iso14443_4a_extra(type_4_tag_get_base_data(data), str);
}
void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str) {
size_t ndef_len = simple_array_get_count(data->ndef_data);
if(ndef_len == 0) {
furi_string_cat_str(str, "No NDEF data to show");
return;
}
const uint8_t* ndef_data = simple_array_cget_data(data->ndef_data);
furi_string_cat_printf(str, "\e*");
for(size_t i = 0; i < ndef_len; i += TYPE_4_TAG_RENDER_BYTES_PER_LINE) {
const uint8_t* line_data = &ndef_data[i];
for(size_t j = 0; j < TYPE_4_TAG_RENDER_BYTES_PER_LINE; j += 2) {
furi_string_cat_printf(str, " %02X%02X", line_data[j], line_data[j + 1]);
}
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <nfc/protocols/type_4_tag/type_4_tag.h>
#include "../nfc_protocol_support_render_common.h"
#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4U)
void nfc_render_type_4_tag_info(
const Type4TagData* data,
NfcProtocolFormatType format_type,
FuriString* str);
void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str);

View File

@@ -1,7 +1,9 @@
#include "nfc_app_i.h" #include "nfc_app_i.h"
#include "api/nfc_app_api_interface.h"
#include "helpers/protocol_support/nfc_protocol_support.h" #include "helpers/protocol_support/nfc_protocol_support.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <loader/firmware_api/firmware_api.h>
bool nfc_custom_event_callback(void* context, uint32_t event) { bool nfc_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);
@@ -49,12 +51,16 @@ NfcApp* nfc_app_alloc(void) {
instance->nfc = nfc_alloc(); instance->nfc = nfc_alloc();
instance->api_resolver = composite_api_resolver_alloc();
composite_api_resolver_add(instance->api_resolver, firmware_api_interface);
composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface);
instance->detected_protocols = nfc_detected_protocols_alloc(); instance->detected_protocols = nfc_detected_protocols_alloc();
instance->felica_auth = felica_auth_alloc(); instance->felica_auth = felica_auth_alloc();
instance->mf_ul_auth = mf_ultralight_auth_alloc(); instance->mf_ul_auth = mf_ultralight_auth_alloc();
instance->slix_unlock = slix_unlock_alloc(); instance->slix_unlock = slix_unlock_alloc();
instance->mfc_key_cache = mf_classic_key_cache_alloc(); instance->mfc_key_cache = mf_classic_key_cache_alloc();
instance->nfc_supported_cards = nfc_supported_cards_alloc(); instance->nfc_supported_cards = nfc_supported_cards_alloc(instance->api_resolver);
// Nfc device // Nfc device
instance->nfc_device = nfc_device_alloc(); instance->nfc_device = nfc_device_alloc();
@@ -148,6 +154,9 @@ void nfc_app_free(NfcApp* instance) {
slix_unlock_free(instance->slix_unlock); slix_unlock_free(instance->slix_unlock);
mf_classic_key_cache_free(instance->mfc_key_cache); mf_classic_key_cache_free(instance->mfc_key_cache);
nfc_supported_cards_free(instance->nfc_supported_cards); nfc_supported_cards_free(instance->nfc_supported_cards);
if(instance->protocol_support) {
nfc_protocol_support_free(instance);
}
// Nfc device // Nfc device
nfc_device_free(instance->nfc_device); nfc_device_free(instance->nfc_device);
@@ -415,6 +424,11 @@ bool nfc_load_from_file_select(NfcApp* instance) {
if(!dialog_file_browser_show( if(!dialog_file_browser_show(
instance->dialogs, instance->file_path, instance->file_path, &browser_options)) instance->dialogs, instance->file_path, instance->file_path, &browser_options))
break; break;
nfc_show_loading_popup(instance, true);
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
nfc_show_loading_popup(instance, false);
success = nfc_load_file(instance, instance->file_path, true); success = nfc_load_file(instance, instance->file_path, true);
} while(!success); } while(!success);
@@ -464,7 +478,7 @@ static bool nfc_is_hal_ready(void) {
static void nfc_show_initial_scene_for_device(NfcApp* nfc) { static void nfc_show_initial_scene_for_device(NfcApp* nfc) {
NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device); NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device);
uint32_t scene = nfc_protocol_support_has_feature( uint32_t scene = nfc_protocol_support_has_feature(
prot, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ? prot, nfc, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ?
NfcSceneEmulate : NfcSceneEmulate :
NfcSceneSavedMenu; NfcSceneSavedMenu;
// Load plugins (parsers) in case if we are in the saved menu // Load plugins (parsers) in case if we are in the saved menu
@@ -523,11 +537,6 @@ int32_t nfc_app(void* p) {
} else { } else {
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
// Load plugins (parsers) one time in case if we running app normally
nfc_show_loading_popup(nfc, true);
nfc_supported_cards_load_cache(nfc->nfc_supported_cards);
nfc_show_loading_popup(nfc, false);
// Switch to the initial scene
scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
} }

View File

@@ -32,10 +32,12 @@
#include "helpers/mfkey32_logger.h" #include "helpers/mfkey32_logger.h"
#include "helpers/nfc_emv_parser.h" #include "helpers/nfc_emv_parser.h"
#include "helpers/mf_classic_key_cache.h" #include "helpers/mf_classic_key_cache.h"
#include "helpers/protocol_support/nfc_protocol_support.h"
#include "helpers/nfc_supported_cards.h" #include "helpers/nfc_supported_cards.h"
#include "helpers/felica_auth.h" #include "helpers/felica_auth.h"
#include "helpers/slix_unlock.h" #include "helpers/slix_unlock.h"
#include <flipper_application/plugins/composite_resolver.h>
#include <loader/loader.h> #include <loader/loader.h>
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <storage/storage.h> #include <storage/storage.h>
@@ -149,6 +151,8 @@ struct NfcApp {
Mfkey32Logger* mfkey32_logger; Mfkey32Logger* mfkey32_logger;
MfUserDict* mf_user_dict; MfUserDict* mf_user_dict;
MfClassicKeyCache* mfc_key_cache; MfClassicKeyCache* mfc_key_cache;
CompositeApiResolver* api_resolver;
NfcProtocolSupport* protocol_support;
NfcSupportedCards* nfc_supported_cards; NfcSupportedCards* nfc_supported_cards;
NfcDevice* nfc_device; NfcDevice* nfc_device;
@@ -176,6 +180,10 @@ typedef enum {
NfcSceneSaveConfirmStateCrackNonces, NfcSceneSaveConfirmStateCrackNonces,
} NfcSceneSaveConfirmState; } NfcSceneSaveConfirmState;
#ifdef __cplusplus
extern "C" {
#endif
int32_t nfc_task(void* p); int32_t nfc_task(void* p);
void nfc_text_store_set(NfcApp* nfc, const char* text, ...); void nfc_text_store_set(NfcApp* nfc, const char* text, ...);
@@ -213,3 +221,7 @@ void nfc_make_app_folder(NfcApp* instance);
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string); void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string);
void nfc_app_run_external(NfcApp* nfc, const char* app_path); void nfc_app_run_external(NfcApp* nfc, const char* app_path);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,149 @@
/*
* Parser for CSC Service Works Reloadable Cash Card (US)
* Date created 2024/5/26
* Zinong Li
* Discord @torron0483
* Github @zinongli
*/
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <bit_lib.h>
#include <furi/core/string.h>
#include <nfc/nfc.h>
#include <nfc/nfc_device.h>
#define TAG "CSC"
bool csc_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
if(data->type != MfClassicType1k) break; // Check card type
// Verify memory format (checksum is later)
const uint8_t refill_block_num = 2;
const uint8_t current_balance_block_num = 4;
const uint8_t current_balance_copy_block_num = 8;
const uint8_t* current_balance_block_start_ptr =
&data->block[current_balance_block_num].data[0];
const uint8_t* current_balance_copy_block_start_ptr =
&data->block[current_balance_copy_block_num].data[0];
uint32_t current_balance_and_times =
bit_lib_bytes_to_num_le(current_balance_block_start_ptr, 4);
uint32_t current_balance_and_times_copy =
bit_lib_bytes_to_num_le(current_balance_copy_block_start_ptr, 4);
// Failed verification if balance != backup
if(current_balance_and_times != current_balance_and_times_copy) {
FURI_LOG_D(TAG, "Backup verification failed");
break;
}
// Even if balance = 0, e.g. new card, refilled times can't be zero
if(current_balance_and_times == 0 || current_balance_and_times_copy == 0) {
FURI_LOG_D(TAG, "Value bytes empty");
break;
}
// Parse data
const uint8_t card_lives_block_num = 9;
const uint8_t refill_sign_block_num = 13;
const uint8_t* refilled_balance_block_start_ptr = &data->block[refill_block_num].data[9];
const uint8_t* refill_times_block_start_ptr = &data->block[refill_block_num].data[5];
const uint8_t* card_lives_block_start_ptr = &data->block[card_lives_block_num].data[0];
const uint8_t* refill_sign_block_start_ptr = &data->block[refill_sign_block_num].data[0];
uint32_t refilled_balance = bit_lib_bytes_to_num_le(refilled_balance_block_start_ptr, 2);
uint32_t refilled_balance_dollar = refilled_balance / 100;
uint8_t refilled_balance_cent = refilled_balance % 100;
uint32_t current_balance = bit_lib_bytes_to_num_le(current_balance_block_start_ptr, 2);
uint32_t current_balance_dollar = current_balance / 100;
uint8_t current_balance_cent = current_balance % 100;
// How many times it can still be used
uint32_t card_lives = bit_lib_bytes_to_num_le(card_lives_block_start_ptr, 2);
uint32_t refill_times = bit_lib_bytes_to_num_le(refill_times_block_start_ptr, 2);
// This is zero when you buy the card. but after refilling it, the refilling machine will leave a non-zero signature here
uint64_t refill_sign = bit_lib_bytes_to_num_le(refill_sign_block_start_ptr, 8);
size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
uint32_t card_uid = bit_lib_bytes_to_num_le(uid, 4);
// Last byte of refill block is checksum
const uint8_t* checksum_block = data->block[refill_block_num].data;
uint8_t xor_result = 0;
for(size_t i = 0; i < 16; ++i) {
xor_result ^= checksum_block[i];
}
if(refill_sign == 0 && refill_times == 1) {
// New cards don't comply to checksum but refill time should be once
furi_string_printf(
parsed_data,
"\e#CSC Service Works\n"
"UID: %lu\n"
"New Card\n"
"Card Value: %lu.%02u USD\n"
"Card Usages Left: %lu",
card_uid,
refilled_balance_dollar,
refilled_balance_cent,
card_lives);
} else {
if(xor_result != 0) {
FURI_LOG_D(TAG, "Checksum failed");
break;
}
furi_string_printf(
parsed_data,
"\e#CSC Service Works\n"
"UID: %lu\n"
"Balance: %lu.%02u USD\n"
"Last Top-up: %lu.%02u USD\n"
"Top-up Count: %lu\n"
"Card Usages Left: %lu",
card_uid,
current_balance_dollar,
current_balance_cent,
refilled_balance_dollar,
refilled_balance_cent,
refill_times,
card_lives);
}
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin csc_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = csc_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor csc_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &csc_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* csc_plugin_ep(void) {
return &csc_plugin_descriptor;
}

View File

@@ -199,6 +199,6 @@ static const FlipperAppPluginDescriptor emv_plugin_descriptor = {
}; };
/* Plugin entry point - must return a pointer to const descriptor */ /* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* emv_plugin_ep() { const FlipperAppPluginDescriptor* emv_plugin_ep(void) {
return &emv_plugin_descriptor; return &emv_plugin_descriptor;
} }

View File

@@ -223,7 +223,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
break; break;
} }
if(!mf_classic_is_card_read(data)) { if(error == MfClassicErrorPartialRead) {
error = mf_classic_poller_sync_read(nfc, &keys_v2, data); error = mf_classic_poller_sync_read(nfc, &keys_v2, data);
if(error == MfClassicErrorNotPresent) { if(error == MfClassicErrorNotPresent) {
FURI_LOG_W(TAG, "Failed to read data: keys_v1"); FURI_LOG_W(TAG, "Failed to read data: keys_v1");
@@ -231,7 +231,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
} }
} }
if(!mf_classic_is_card_read(data)) { if(error == MfClassicErrorPartialRead) {
error = mf_classic_poller_sync_read(nfc, &keys_v3, data); error = mf_classic_poller_sync_read(nfc, &keys_v3, data);
if(error == MfClassicErrorNotPresent) { if(error == MfClassicErrorNotPresent) {
FURI_LOG_W(TAG, "Failed to read data: keys_v3"); FURI_LOG_W(TAG, "Failed to read data: keys_v3");
@@ -241,7 +241,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
nfc_device_set_data(device, NfcProtocolMfClassic, data); nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data); is_read = (error == MfClassicErrorNone);
} while(false); } while(false);
mf_classic_free(data); mf_classic_free(data);
@@ -409,6 +409,6 @@ static const FlipperAppPluginDescriptor kazan_plugin_descriptor = {
}; };
/* Plugin entry point - must return a pointer to const descriptor */ /* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* kazan_plugin_ep() { const FlipperAppPluginDescriptor* kazan_plugin_ep(void) {
return &kazan_plugin_descriptor; return &kazan_plugin_descriptor;
} }

View File

@@ -185,6 +185,6 @@ static const FlipperAppPluginDescriptor metromoney_plugin_descriptor = {
}; };
/* Plugin entry point - must return a pointer to const descriptor */ /* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* metromoney_plugin_ep() { const FlipperAppPluginDescriptor* metromoney_plugin_ep(void) {
return &metromoney_plugin_descriptor; return &metromoney_plugin_descriptor;
} }

View File

@@ -199,7 +199,7 @@ static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) {
MfClassicSectorTrailer* sec_tr = MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector); mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector);
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6); uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
if(key != cfg.keys[cfg.verify_sector].b) return false; if(key != cfg.keys[cfg.verify_sector].b) break;
//Get UID //Get UID
uint8_t uid[UID_LENGTH]; uint8_t uid[UID_LENGTH];

View File

@@ -20,6 +20,7 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight.h> #include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_classic/mf_classic.h> #include <nfc/protocols/mf_classic/mf_classic.h>
#include <nfc/protocols/slix/slix.h> #include <nfc/protocols/slix/slix.h>
#include <nfc/protocols/type_4_tag/type_4_tag.h>
#include <bit_lib.h> #include <bit_lib.h>
#include <toolbox/pretty_format.h> #include <toolbox/pretty_format.h>
@@ -31,7 +32,8 @@
#define NDEF_PROTO_UL (1) #define NDEF_PROTO_UL (1)
#define NDEF_PROTO_MFC (2) #define NDEF_PROTO_MFC (2)
#define NDEF_PROTO_SLIX (3) #define NDEF_PROTO_SLIX (3)
#define NDEF_PROTO_TOTAL (4) #define NDEF_PROTO_T4T (4)
#define NDEF_PROTO_TOTAL (5)
#ifndef NDEF_PROTO #ifndef NDEF_PROTO
#error Must specify what protocol to use with NDEF_PROTO define! #error Must specify what protocol to use with NDEF_PROTO define!
@@ -40,10 +42,10 @@
#error Invalid NDEF_PROTO specified! #error Invalid NDEF_PROTO specified!
#endif #endif
#define NDEF_TITLE(device, parsed_data) \ #define NDEF_TITLE(device, parsed_data) \
furi_string_printf( \ furi_string_printf( \
parsed_data, \ parsed_data, \
"\e#NDEF Format Data\nCard type: %s\n", \ "\e#NDEF Format Data\nCard: %s\n", \
nfc_device_get_name(device, NfcDeviceNameTypeFull)) nfc_device_get_name(device, NfcDeviceNameTypeFull))
// ---=== structures ===--- // ---=== structures ===---
@@ -151,6 +153,11 @@ typedef struct {
const uint8_t* start; const uint8_t* start;
size_t size; size_t size;
} slix; } slix;
#elif NDEF_PROTO == NDEF_PROTO_T4T
struct {
const uint8_t* data;
size_t size;
} t4t;
#endif #endif
} Ndef; } Ndef;
@@ -230,6 +237,13 @@ static bool ndef_get(Ndef* ndef, size_t pos, size_t len, void* buf) {
memcpy(buf, ndef->slix.start + pos, len); memcpy(buf, ndef->slix.start + pos, len);
return true; return true;
#elif NDEF_PROTO == NDEF_PROTO_T4T
// Memory space is contiguous, simply need to remap to data pointer
if(pos + len > ndef->t4t.size) return false;
memcpy(buf, ndef->t4t.data + pos, len);
return true;
#else #else
UNUSED(ndef); UNUSED(ndef);
@@ -1039,6 +1053,44 @@ static bool ndef_slix_parse(const NfcDevice* device, FuriString* parsed_data) {
return parsed > 0; return parsed > 0;
} }
#elif NDEF_PROTO == NDEF_PROTO_T4T
static bool ndef_t4t_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
size_t data_start = 0;
size_t data_size = simple_array_get_count(data->ndef_data);
NDEF_TITLE(device, parsed_data);
furi_string_replace(parsed_data, "Card: ", "Protocol: ");
if(data->is_tag_specific && !furi_string_empty(data->platform_name)) {
furi_string_cat_printf(
parsed_data, "Card: %s\n", furi_string_get_cstr(data->platform_name));
}
Ndef ndef = {
.output = parsed_data,
.t4t =
{
.data = data_size == 0 ? NULL : simple_array_cget_data(data->ndef_data),
.size = data_size,
},
};
size_t parsed = ndef_parse_message(&ndef, data_start, data_size - data_start, 1, false);
if(parsed) {
furi_string_trim(parsed_data, "\n");
furi_string_cat(parsed_data, "\n");
} else {
furi_string_reset(parsed_data);
}
return parsed > 0;
}
#endif #endif
// ---=== boilerplate ===--- // ---=== boilerplate ===---
@@ -1056,6 +1108,9 @@ static const NfcSupportedCardsPlugin ndef_plugin = {
#elif NDEF_PROTO == NDEF_PROTO_SLIX #elif NDEF_PROTO == NDEF_PROTO_SLIX
.parse = ndef_slix_parse, .parse = ndef_slix_parse,
.protocol = NfcProtocolSlix, .protocol = NfcProtocolSlix,
#elif NDEF_PROTO == NDEF_PROTO_T4T
.parse = ndef_t4t_parse,
.protocol = NfcProtocolType4Tag,
#endif #endif
}; };

View File

@@ -247,7 +247,7 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) {
// Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards. // Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards.
furi_string_cat_printf(parsed_data, "Number: "); furi_string_cat_printf(parsed_data, "Number: ");
FuriString* card_number_s = furi_string_alloc(); FuriString* card_number_s = furi_string_alloc();
furi_string_cat_printf(card_number_s, "%llu", card_number); furi_string_cat_printf(card_number_s, "%lld", card_number);
FuriString* tmp_s = furi_string_alloc_set_str("9643 3078 "); FuriString* tmp_s = furi_string_alloc_set_str("9643 3078 ");
for(uint8_t i = 0; i < 24; i += 4) { for(uint8_t i = 0; i < 24; i += 4) {
for(uint8_t j = 0; j < 4; j++) { for(uint8_t j = 0; j < 4; j++) {

View File

@@ -0,0 +1,334 @@
#include "nfc_supported_card_plugin.h"
#include <bit_lib.h>
#include <flipper_application.h>
#include <furi.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <string.h>
#define MAX_TRIPS 10
#define TAG "SmartRider"
#define MAX_BLOCKS 64
#define MAX_DATE_ITERATIONS 366
static const uint8_t STANDARD_KEYS[3][6] = {
{0x20, 0x31, 0xD1, 0xE5, 0x7A, 0x3B},
{0x4C, 0xA6, 0x02, 0x9F, 0x94, 0x73},
{0x19, 0x19, 0x53, 0x98, 0xE3, 0x2F}};
typedef struct {
uint32_t timestamp;
uint16_t cost;
uint16_t transaction_number;
uint16_t journey_number;
char route[5];
uint8_t tap_on : 1;
uint8_t block;
} __attribute__((packed)) TripData;
typedef struct {
uint32_t balance;
uint16_t issued_days;
uint16_t expiry_days;
uint16_t purchase_cost;
uint16_t auto_load_threshold;
uint16_t auto_load_value;
char card_serial_number[11];
uint8_t token;
TripData trips[MAX_TRIPS];
uint8_t trip_count;
} __attribute__((packed)) SmartRiderData;
static const char* const CONCESSION_TYPES[] = {
"Pre-issue",
"Standard Fare",
"Student",
NULL,
"Tertiary",
NULL,
"Seniors",
"Health Care",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"PTA Staff",
"Pensioner",
"Free Travel"};
static inline const char* get_concession_type(uint8_t token) {
return (token <= 0x10) ? CONCESSION_TYPES[token] : "Unknown";
}
static bool authenticate_and_read(
Nfc* nfc,
uint8_t sector,
const uint8_t* key,
MfClassicKeyType key_type,
MfClassicBlock* block_data) {
MfClassicKey mf_key;
memcpy(mf_key.data, key, 6);
uint8_t block = mf_classic_get_first_block_num_of_sector(sector);
if(mf_classic_poller_sync_auth(nfc, block, &mf_key, key_type, NULL) != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Authentication failed for sector %d key type %d", sector, key_type);
return false;
}
if(mf_classic_poller_sync_read_block(nfc, block, &mf_key, key_type, block_data) !=
MfClassicErrorNone) {
FURI_LOG_D(TAG, "Read failed for sector %d", sector);
return false;
}
return true;
}
static bool smartrider_verify(Nfc* nfc) {
furi_assert(nfc);
MfClassicBlock block_data;
for(int i = 0; i < 3; i++) {
if(!authenticate_and_read(
nfc,
i * 6,
STANDARD_KEYS[i],
i % 2 == 0 ? MfClassicKeyTypeA : MfClassicKeyTypeB,
&block_data) ||
memcmp(block_data.data, STANDARD_KEYS[i], 6) != 0) {
FURI_LOG_D(TAG, "Authentication or key mismatch for key %d", i);
return false;
}
}
FURI_LOG_I(TAG, "SmartRider card verified");
return true;
}
static inline bool
parse_trip_data(const MfClassicBlock* block_data, TripData* trip, uint8_t block_number) {
trip->timestamp = bit_lib_bytes_to_num_le(block_data->data + 3, 4);
trip->tap_on = (block_data->data[7] & 0x10) == 0x10;
memcpy(trip->route, block_data->data + 8, 4);
trip->route[4] = '\0';
trip->cost = bit_lib_bytes_to_num_le(block_data->data + 13, 2);
trip->transaction_number = bit_lib_bytes_to_num_le(block_data->data, 2);
trip->journey_number = bit_lib_bytes_to_num_le(block_data->data + 2, 2);
trip->block = block_number;
return true;
}
static bool smartrider_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
MfClassicType type;
if(mf_classic_poller_sync_detect_type(nfc, &type) != MfClassicErrorNone ||
type != MfClassicType1k) {
mf_classic_free(data);
return false;
}
data->type = type;
MfClassicDeviceKeys keys = {.key_a_mask = 0, .key_b_mask = 0};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
memcpy(keys.key_a[i].data, STANDARD_KEYS[i == 0 ? 0 : 1], sizeof(STANDARD_KEYS[0]));
if(i > 0) {
memcpy(keys.key_b[i].data, STANDARD_KEYS[2], sizeof(STANDARD_KEYS[0]));
FURI_BIT_SET(keys.key_b_mask, i);
}
FURI_BIT_SET(keys.key_a_mask, i);
}
MfClassicError error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
mf_classic_free(data);
return false;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
mf_classic_free(data);
return true;
}
static bool is_leap_year(uint16_t year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
static void calculate_date(uint32_t timestamp, char* date_str, size_t date_str_size) {
uint32_t seconds_since_2000 = timestamp;
uint32_t days_since_2000 = seconds_since_2000 / 86400;
uint16_t year = 2000;
uint8_t month = 1;
uint16_t day = 1;
static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
while(days_since_2000 >= (is_leap_year(year) ? 366 : 365)) {
days_since_2000 -= (is_leap_year(year) ? 366 : 365);
year++;
}
for(month = 0; month < 12; month++) {
uint16_t dim = days_in_month[month];
if(month == 1 && is_leap_year(year)) {
dim++;
}
if(days_since_2000 < dim) {
break;
}
days_since_2000 -= dim;
}
day = days_since_2000 + 1;
month++; // Adjust month to 1-based
if(date_str_size > 0) {
size_t written = 0;
written += snprintf(date_str + written, date_str_size - written, "%02u", day);
if(written < date_str_size - 1) {
written += snprintf(date_str + written, date_str_size - written, "/");
}
if(written < date_str_size - 1) {
written += snprintf(date_str + written, date_str_size - written, "%02u", month);
}
if(written < date_str_size - 1) {
written += snprintf(date_str + written, date_str_size - written, "/");
}
if(written < date_str_size - 1) {
snprintf(date_str + written, date_str_size - written, "%02u", year % 100);
}
} else {
// If the buffer size is 0, do nothing
}
}
static bool smartrider_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
SmartRiderData sr_data = {0};
if(data->type != MfClassicType1k) {
FURI_LOG_E(TAG, "Invalid card type");
return false;
}
const MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
if(!sec_tr || memcmp(sec_tr->key_a.data, STANDARD_KEYS[0], 6) != 0) {
FURI_LOG_E(TAG, "Key verification failed for sector 0");
return false;
}
static const uint8_t required_blocks[] = {14, 4, 5, 1, 52, 50, 0};
for(size_t i = 0; i < COUNT_OF(required_blocks); i++) {
if(required_blocks[i] >= MAX_BLOCKS ||
!mf_classic_is_block_read(data, required_blocks[i])) {
FURI_LOG_E(TAG, "Required block %d is not read or out of range", required_blocks[i]);
return false;
}
}
sr_data.balance = bit_lib_bytes_to_num_le(data->block[14].data + 7, 2);
sr_data.issued_days = bit_lib_bytes_to_num_le(data->block[4].data + 16, 2);
sr_data.expiry_days = bit_lib_bytes_to_num_le(data->block[4].data + 18, 2);
sr_data.auto_load_threshold = bit_lib_bytes_to_num_le(data->block[4].data + 20, 2);
sr_data.auto_load_value = bit_lib_bytes_to_num_le(data->block[4].data + 22, 2);
sr_data.token = data->block[5].data[8];
sr_data.purchase_cost = bit_lib_bytes_to_num_le(data->block[0].data + 14, 2);
snprintf(
sr_data.card_serial_number,
sizeof(sr_data.card_serial_number),
"%02X%02X%02X%02X%02X",
data->block[1].data[6],
data->block[1].data[7],
data->block[1].data[8],
data->block[1].data[9],
data->block[1].data[10]);
for(uint8_t block_number = 40; block_number <= 52 && sr_data.trip_count < MAX_TRIPS;
block_number++) {
if((block_number != 43 && block_number != 47 && block_number != 51) &&
mf_classic_is_block_read(data, block_number) &&
parse_trip_data(
&data->block[block_number], &sr_data.trips[sr_data.trip_count], block_number)) {
sr_data.trip_count++;
}
}
// Sort trips by timestamp (descending order)
for(uint8_t i = 0; i < sr_data.trip_count - 1; i++) {
for(uint8_t j = 0; j < sr_data.trip_count - i - 1; j++) {
if(sr_data.trips[j].timestamp < sr_data.trips[j + 1].timestamp) {
TripData temp = sr_data.trips[j];
sr_data.trips[j] = sr_data.trips[j + 1];
sr_data.trips[j + 1] = temp;
}
}
}
furi_string_printf(
parsed_data,
"\e#SmartRider\nBalance: $%lu.%02lu\nConcession: %s\nSerial: %s%s\n"
"Total Cost: $%u.%02u\nAuto-Load: $%u.%02u/$%u.%02u\n\e#Tag On/Off History\n",
sr_data.balance / 100,
sr_data.balance % 100,
get_concession_type(sr_data.token),
memcmp(sr_data.card_serial_number, "00", 2) == 0 ? "SR0" : "",
memcmp(sr_data.card_serial_number, "00", 2) == 0 ? sr_data.card_serial_number + 2 :
sr_data.card_serial_number,
sr_data.purchase_cost / 100,
sr_data.purchase_cost % 100,
sr_data.auto_load_threshold / 100,
sr_data.auto_load_threshold % 100,
sr_data.auto_load_value / 100,
sr_data.auto_load_value % 100);
for(uint8_t i = 0; i < sr_data.trip_count; i++) {
char date_str[9];
calculate_date(sr_data.trips[i].timestamp, date_str, sizeof(date_str));
uint32_t cost = sr_data.trips[i].cost;
if(cost > 0) {
furi_string_cat_printf(
parsed_data,
"%s %c $%lu.%02lu %s\n",
date_str,
sr_data.trips[i].tap_on ? '+' : '-',
cost / 100,
cost % 100,
sr_data.trips[i].route);
} else {
furi_string_cat_printf(
parsed_data,
"%s %c %s\n",
date_str,
sr_data.trips[i].tap_on ? '+' : '-',
sr_data.trips[i].route);
}
}
return true;
}
static const NfcSupportedCardsPlugin smartrider_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = smartrider_verify,
.read = smartrider_read,
.parse = smartrider_parse,
};
__attribute__((used)) const FlipperAppPluginDescriptor* smartrider_plugin_ep() {
static const FlipperAppPluginDescriptor plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &smartrider_plugin,
};
return &plugin_descriptor;
}
// made with love by jay candel <3

View File

@@ -0,0 +1,111 @@
// Parser for Philips Sonicare toothbrush heads.
// Made by @Sil333033
// Thanks to Cyrill Künzi for this research! https://kuenzi.dev/toothbrush/
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#define TAG "Sonicare"
typedef enum {
SonicareHeadWhite,
SonicareHeadBlack,
SonicareHeadUnkown,
} SonicareHead;
static SonicareHead sonicare_get_head_type(const MfUltralightData* data) {
// data.page[34].data got 4 bytes
// 31:32:31:34 for black (not sure)
// 31:31:31:31 for white (not sure)
// the data should be in here based on the research, but i cant find it.
// page 34 byte 0 is always 0x30 for the white brushes i have, so i guess thats white
// TODO: Get a black brush and test this
if(data->page[34].data[0] == 0x30) {
return SonicareHeadWhite;
} else {
return SonicareHeadUnkown;
}
}
static uint32_t sonicare_get_seconds_brushed(const MfUltralightData* data) {
uint32_t seconds_brushed = 0;
seconds_brushed += data->page[36].data[0];
seconds_brushed += data->page[36].data[1] << 8;
return seconds_brushed;
}
static bool sonicare_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
bool parsed = false;
do {
// Check for NDEF link match
const char* test = "philips.com/nfcbrushheadtap";
// Data is a array of arrays, cast to char array and compare
if(strncmp(test, (const char*)&data->page[5].data[3], strlen(test)) != 0) {
FURI_LOG_D(TAG, "Not a Philips Sonicare head");
break;
}
const SonicareHead head_type = sonicare_get_head_type(data);
const uint32_t seconds_brushed = sonicare_get_seconds_brushed(data);
FuriString* head_type_str = furi_string_alloc();
switch(head_type) {
case SonicareHeadWhite:
furi_string_printf(head_type_str, "White");
break;
case SonicareHeadBlack:
furi_string_printf(head_type_str, "Black");
break;
case SonicareHeadUnkown:
default:
furi_string_printf(head_type_str, "Unknown");
break;
}
furi_string_printf(
parsed_data,
"\e#Philips Sonicare head\nColor: %s\nTime brushed: %02.0f:%02.0f:%02ld\n",
furi_string_get_cstr(head_type_str),
floor(seconds_brushed / 3600),
floor((seconds_brushed / 60) % 60),
seconds_brushed % 60);
furi_string_free(head_type_str);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin sonicare_plugin = {
.protocol = NfcProtocolMfUltralight,
.verify = NULL,
.read = NULL,
.parse = sonicare_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor sonicare_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &sonicare_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* sonicare_plugin_ep(void) {
return &sonicare_plugin_descriptor;
}

View File

@@ -112,7 +112,7 @@ static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) {
// Verify key // Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
if(key != two_cities_4k_keys[4].a) return false; if(key != two_cities_4k_keys[4].a) break;
// ===== // =====
// PLANTAIN // PLANTAIN

View File

@@ -0,0 +1,292 @@
// Parser for CTA Ventra Ultralight cards
// Made by @hazardousvoltage
// Based on my own research, with...
// Credit to https://www.lenrek.net/experiments/compass-tickets/ & MetroDroid project for underlying info
//
// This parser can decode the paper single-use and single/multi-day paper passes using Ultralight EV1
// The plastic cards are DESFire and fully locked down, not much useful info extractable
// TODO:
// - Sort the duplicate/rare ticket types
// - Database of stop IDs for trains? Buses there's just too damn many, but you can find them here:
// https://data.cityofchicago.org/Transportation/CTA-Bus-Stops-kml/84eu-buny/about_data
// - Generalize to handle all known Cubic Nextfare Ultralight systems? Anyone wants to send me specimen dumps, hit me up on Discord.
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include "datetime.h"
#include <furi_hal.h>
#define TAG "Ventra"
DateTime ventra_exp_date = {0}, ventra_validity_date = {0};
uint8_t ventra_high_seq = 0, ventra_cur_blk = 0, ventra_mins_active = 0;
uint32_t time_now() {
return furi_hal_rtc_get_timestamp();
}
static DateTime dt_delta(DateTime dt, uint8_t delta_days) {
// returns shifted DateTime, from initial DateTime and time offset in seconds
DateTime dt_shifted = {0};
datetime_timestamp_to_datetime(
datetime_datetime_to_timestamp(&dt) - (uint64_t)delta_days * 86400, &dt_shifted);
return dt_shifted;
}
/*
static long dt_diff(DateTime dta, DateTime dtb) {
// returns difference in seconds between two DateTimes
long diff;
diff = datetime_datetime_to_timestamp(&dta) - datetime_datetime_to_timestamp(&dtb);
return diff;
}
*/
// Card is expired if:
// - Hard expiration date passed (90 days from purchase, encoded in product record)
// - Soft expiration date passed:
// - For passes, n days after first use
// - For tickets, 2 hours after first use
// Calculating these is dumber than it needs to be, see xact record parser.
bool isExpired(void) {
uint32_t ts_hard_exp = datetime_datetime_to_timestamp(&ventra_exp_date);
uint32_t ts_soft_exp = datetime_datetime_to_timestamp(&ventra_validity_date);
uint32_t ts_now = time_now();
return (ts_now >= ts_hard_exp || ts_now > ts_soft_exp);
}
static FuriString* ventra_parse_xact(const MfUltralightData* data, uint8_t blk, bool is_pass) {
FuriString* ventra_xact_str = furi_string_alloc();
uint16_t ts = data->page[blk].data[0] | data->page[blk].data[1] << 8;
uint8_t tran_type = ts & 0x1F;
ts >>= 5;
uint8_t day = data->page[blk].data[2];
uint32_t work = data->page[blk + 1].data[0] | data->page[blk + 1].data[1] << 8 |
data->page[blk + 1].data[2] << 16;
uint8_t seq = work & 0x7F;
uint16_t exp = (work >> 7) & 0x7FF;
uint8_t exp_day = data->page[blk + 2].data[0];
uint16_t locus = data->page[blk + 2].data[1] | data->page[blk + 2].data[2] << 8;
uint8_t line = data->page[blk + 2].data[3];
// This computes the block timestamp, based on the card expiration date and delta from it
DateTime dt = dt_delta(ventra_exp_date, day);
dt.hour = (ts & 0x7FF) / 60;
dt.minute = (ts & 0x7FF) % 60;
// If sequence is 0, block isn't used yet (new card with only one active block, typically the first one.
// Otherwise, the block with higher sequence is the latest transaction, and the other block is prior transaction.
// Not necessarily in that order on the card. We need the latest data to compute validity and pretty-print them
// in reverse chrono. So this mess sets some globals as to which block is current, computes the validity times, etc.
if(seq == 0) {
furi_string_printf(ventra_xact_str, "-- EMPTY --");
return (ventra_xact_str);
}
if(seq > ventra_high_seq) {
ventra_high_seq = seq;
ventra_cur_blk = blk;
ventra_mins_active = data->page[blk + 1].data[3];
// Figure out the soft expiration. For passes it's easy, the readers update the "exp" field in the transaction record.
// Tickets, not so much, readers don't update "exp", but each xact record has "minutes since last tap" which is
// updated and carried forward. That, plus transaction timestamp, gives the expiration time.
if(tran_type == 6) { // Furthermore, purchase transactions set bogus expiration dates
if(is_pass) {
ventra_validity_date = dt_delta(ventra_exp_date, exp_day);
ventra_validity_date.hour = (exp & 0x7FF) / 60;
ventra_validity_date.minute = (exp & 0x7FF) % 60;
} else {
uint32_t validity_ts = datetime_datetime_to_timestamp(&dt);
validity_ts += (120 - ventra_mins_active) * 60;
datetime_timestamp_to_datetime(validity_ts, &ventra_validity_date);
}
}
}
// Type 0 = Purchase, 1 = Train ride, 2 = Bus ride
// TODO: Check PACE and see if it uses a different line code
char linemap[3] = "PTB";
char* xact_fmt = (line == 2) ? "%c %5d %04d-%02d-%02d %02d:%02d" :
"%c %04X %04d-%02d-%02d %02d:%02d";
// I like a nice concise display showing all the relevant infos without having to scroll...
// Line StopID DateTime
furi_string_printf(
ventra_xact_str,
xact_fmt,
(line < 3) ? linemap[line] : '?',
locus,
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute);
return (ventra_xact_str);
}
static bool ventra_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
bool parsed = false;
do {
// This test can probably be improved -- it matches every Ventra I've seen, but will also match others
// in the same family. Or maybe we just generalize this parser.
if(data->page[4].data[0] != 0x0A || data->page[4].data[1] != 4 ||
data->page[4].data[2] != 0 || data->page[6].data[0] != 0 ||
data->page[6].data[1] != 0 || data->page[6].data[2] != 0) {
FURI_LOG_D(TAG, "Not Ventra Ultralight");
break;
}
// Parse the product record, display interesting data & extract info needed to parse transaction blocks
// Had this in its own function, ended up just setting a bunch of shitty globals, so inlined it instead.
FuriString* ventra_prod_str = furi_string_alloc();
uint8_t otp = data->page[3].data[0];
uint8_t prod_code = data->page[5].data[2];
bool is_pass = false;
switch(prod_code) {
case 2:
case 0x1F: // Only ever seen one of these, it parses like a Single
furi_string_cat_printf(ventra_prod_str, "Single");
break;
case 3:
case 0x3F:
is_pass = true;
furi_string_cat_printf(ventra_prod_str, "1-Day");
break;
case 4: // Last I checked, 3 day passes only available at airport TVMs & social service agencies
is_pass = true;
furi_string_cat_printf(ventra_prod_str, "3-Day");
break;
default:
is_pass =
true; // There are some card types I don't know what they are, but they parse like a pass, not a ticket.
furi_string_cat_printf(ventra_prod_str, "0x%02X", data->page[5].data[2]);
break;
}
uint16_t date_y = data->page[4].data[3] | (data->page[5].data[0] << 8);
uint8_t date_d = date_y & 0x1F;
uint8_t date_m = (date_y >> 5) & 0x0F;
date_y >>= 9;
date_y += 2000;
ventra_exp_date.day = date_d;
ventra_exp_date.month = date_m;
ventra_exp_date.year = date_y;
ventra_validity_date = ventra_exp_date; // Until we know otherwise
// Parse the transaction blocks. This sets a few sloppy globals, but it's too complex and repetitive to inline.
FuriString* ventra_xact_str1 = ventra_parse_xact(data, 8, is_pass);
FuriString* ventra_xact_str2 = ventra_parse_xact(data, 12, is_pass);
uint8_t card_state = 1;
uint8_t rides_left = 0;
char* card_states[5] = {"???", "NEW", "ACT", "USED", "EXP"};
if(ventra_high_seq > 1) card_state = 2;
// On "ticket" product, the OTP bits mark off rides used. Bit 0 seems to be unused, the next 3 are set as rides are used.
// Some, not all, readers will set the high bits to 0x7 when a card is tapped after it's expired or depleted. Have not
// seen other combinations, but if we do, we'll make a nice ???. 1-day passes set the OTP bit 1 on first use. 3-day
// passes do not. But we don't really care, since they don't matter on passes, unless you're trying to rollback one.
if(!is_pass) {
switch(otp) {
case 0:
rides_left = 3;
break;
case 2:
card_state = 2;
rides_left = 2;
break;
case 6:
card_state = 2;
rides_left = 1;
break;
case 0x0E:
case 0x7E:
card_state = 3;
rides_left = 0;
break;
default:
card_state = 0;
rides_left = 0;
break;
}
}
if(isExpired()) {
card_state = 4;
rides_left = 0;
}
furi_string_printf(
parsed_data,
"\e#Ventra %s (%s)\n",
furi_string_get_cstr(ventra_prod_str),
card_states[card_state]);
furi_string_cat_printf(
parsed_data,
"Exp: %04d-%02d-%02d %02d:%02d\n",
ventra_validity_date.year,
ventra_validity_date.month,
ventra_validity_date.day,
ventra_validity_date.hour,
ventra_validity_date.minute);
if(rides_left) {
furi_string_cat_printf(parsed_data, "Rides left: %d\n", rides_left);
}
furi_string_cat_printf(
parsed_data,
"%s\n",
furi_string_get_cstr(ventra_cur_blk == 8 ? ventra_xact_str1 : ventra_xact_str2));
furi_string_cat_printf(
parsed_data,
"%s\n",
furi_string_get_cstr(ventra_cur_blk == 8 ? ventra_xact_str2 : ventra_xact_str1));
furi_string_cat_printf(
parsed_data, "TVM ID: %02X%02X\n", data->page[7].data[1], data->page[7].data[0]);
furi_string_cat_printf(parsed_data, "Tx count: %d\n", ventra_high_seq);
furi_string_cat_printf(
parsed_data,
"Hard Expiry: %04d-%02d-%02d",
ventra_exp_date.year,
ventra_exp_date.month,
ventra_exp_date.day);
furi_string_free(ventra_prod_str);
furi_string_free(ventra_xact_str1);
furi_string_free(ventra_xact_str2);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin ventra_plugin = {
.protocol = NfcProtocolMfUltralight,
.verify = NULL,
.read = NULL,
.parse = ventra_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor ventra_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &ventra_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* ventra_plugin_ep(void) {
return &ventra_plugin_descriptor;
}

View File

@@ -209,6 +209,6 @@ static const FlipperAppPluginDescriptor zolotaya_korona_plugin_descriptor = {
}; };
/* Plugin entry point - must return a pointer to const descriptor */ /* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep() { const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep(void) {
return &zolotaya_korona_plugin_descriptor; return &zolotaya_korona_plugin_descriptor;
} }

View File

@@ -139,6 +139,6 @@ static const FlipperAppPluginDescriptor zolotaya_korona_online_plugin_descriptor
}; };
/* Plugin entry point - must return a pointer to const descriptor */ /* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep() { const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep(void) {
return &zolotaya_korona_online_plugin_descriptor; return &zolotaya_korona_online_plugin_descriptor;
} }

View File

@@ -18,6 +18,7 @@ ADD_SCENE(nfc, extra_actions, ExtraActions)
ADD_SCENE(nfc, read_success, ReadSuccess) ADD_SCENE(nfc, read_success, ReadSuccess)
ADD_SCENE(nfc, read_menu, ReadMenu) ADD_SCENE(nfc, read_menu, ReadMenu)
ADD_SCENE(nfc, emulate, Emulate) ADD_SCENE(nfc, emulate, Emulate)
ADD_SCENE(nfc, write, Write)
ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, rpc, Rpc)
ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, debug, Debug)
ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, field, Field)
@@ -25,10 +26,6 @@ ADD_SCENE(nfc, retry_confirm, RetryConfirm)
ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm)
ADD_SCENE(nfc, save_confirm, SaveConfirm) ADD_SCENE(nfc, save_confirm, SaveConfirm)
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_menu, MfUltralightUnlockMenu)
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
@@ -48,10 +45,6 @@ ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete)
ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial)
ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess)
ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard) ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard)
ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial)
ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess)
ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail)
ADD_SCENE(nfc, mf_classic_write_initial_wrong_card, MfClassicWriteInitialWrongCard)
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)

View File

@@ -16,6 +16,10 @@ void nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) {
void nfc_scene_detect_on_enter(void* context) { void nfc_scene_detect_on_enter(void* context) {
NfcApp* instance = context; NfcApp* instance = context;
nfc_show_loading_popup(instance, true);
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
nfc_show_loading_popup(instance, false);
// Setup view // Setup view
popup_reset(instance->popup); popup_reset(instance->popup);
popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop); popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop);

View File

@@ -236,7 +236,6 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
dict_attack_set_card_state(instance->dict_attack, true); dict_attack_set_card_state(instance->dict_attack, true);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);
nfc_blink_read_start(instance); nfc_blink_read_start(instance);
notification_message(instance->notifications, &sequence_display_backlight_enforce_on);
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
@@ -392,5 +391,4 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
} }
nfc_blink_stop(instance); nfc_blink_stop(instance);
notification_message(instance->notifications, &sequence_display_backlight_enforce_auto);
} }

View File

@@ -47,6 +47,9 @@ bool nfc_scene_mf_classic_update_initial_wrong_card_on_event(
if(event.event == GuiButtonTypeLeft) { if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(instance->scene_manager); consumed = scene_manager_previous_scene(instance->scene_manager);
} }
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, NfcSceneSavedMenu);
} }
return consumed; return consumed;
} }

View File

@@ -1,148 +0,0 @@
#include "../nfc_app_i.h"
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
enum {
NfcSceneMfClassicWriteInitialStateCardSearch,
NfcSceneMfClassicWriteInitialStateCardFound,
};
NfcCommand
nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolMfClassic);
NfcCommand command = NfcCommandContinue;
NfcApp* instance = context;
MfClassicPollerEvent* mfc_event = event.event_data;
const MfClassicData* write_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
const MfClassicData* tag_data = nfc_poller_get_data(instance->poller);
if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) {
mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite;
} else {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
command = NfcCommandStop;
}
} else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) {
uint8_t sector = mfc_event->data->sec_tr_data.sector_num;
uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector);
if(mf_classic_is_block_read(write_data, sec_tr)) {
mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr];
mfc_event->data->sec_tr_data.sector_trailer_provided = true;
} else {
mfc_event->data->sec_tr_data.sector_trailer_provided = false;
}
} else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) {
uint8_t block_num = mfc_event->data->write_block_data.block_num;
if(mf_classic_is_block_read(write_data, block_num)) {
mfc_event->data->write_block_data.write_block = write_data->block[block_num];
mfc_event->data->write_block_data.write_block_provided = true;
} else {
mfc_event->data->write_block_data.write_block_provided = false;
}
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
} else if(mfc_event->type == MfClassicPollerEventTypeFail) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
}
return command;
}
static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) {
Popup* popup = instance->popup;
popup_reset(popup);
uint32_t state =
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial);
if(state == NfcSceneMfClassicWriteInitialStateCardSearch) {
popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter);
popup_set_text(
instance->popup, "Use the source\ncard only", 95, 38, AlignCenter, 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_classic_write_initial_on_enter(void* context) {
NfcApp* instance = context;
dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicWriteInitial,
NfcSceneMfClassicWriteInitialStateCardSearch);
nfc_scene_mf_classic_write_initial_setup_view(instance);
// Setup and start worker
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(
instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance);
nfc_blink_emulate_start(instance);
}
bool nfc_scene_mf_classic_write_initial_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,
NfcSceneMfClassicWriteInitial,
NfcSceneMfClassicWriteInitialStateCardFound);
nfc_scene_mf_classic_write_initial_setup_view(instance);
consumed = true;
} else if(event.event == NfcCustomEventCardLost) {
scene_manager_set_scene_state(
instance->scene_manager,
NfcSceneMfClassicWriteInitial,
NfcSceneMfClassicWriteInitialStateCardSearch);
nfc_scene_mf_classic_write_initial_setup_view(instance);
consumed = true;
} else if(event.event == NfcCustomEventWrongCard) {
scene_manager_next_scene(
instance->scene_manager, NfcSceneMfClassicWriteInitialWrongCard);
consumed = true;
} else if(event.event == NfcCustomEventPollerSuccess) {
scene_manager_next_scene(
instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess);
consumed = true;
} else if(event.event == NfcCustomEventPollerFailure) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_classic_write_initial_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,
NfcSceneMfClassicWriteInitial,
NfcSceneMfClassicWriteInitialStateCardSearch);
// Clear view
popup_reset(instance->popup);
nfc_blink_stop(instance);
}

View File

@@ -1,62 +0,0 @@
#include "../nfc_app_i.h"
void nfc_scene_mf_classic_write_initial_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_classic_write_initial_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,
"Not all sectors\nwere written\ncorrectly.");
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Finish",
nfc_scene_mf_classic_write_initial_fail_widget_callback,
instance);
// Setup and start worker
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) {
NfcApp* instance = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, NfcSceneSavedMenu);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, NfcSceneSavedMenu);
}
return consumed;
}
void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) {
NfcApp* instance = context;
widget_reset(instance->widget);
}

View File

@@ -1,43 +0,0 @@
#include "../nfc_app_i.h"
void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) {
NfcApp* instance = context;
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) {
NfcApp* instance = context;
dolphin_deed(DolphinDeedNfcSave);
notification_message(instance->notifications, &sequence_success);
Popup* popup = instance->popup;
popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_timeout(popup, 1500);
popup_set_context(popup, instance);
popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
}
bool nfc_scene_mf_classic_write_initial_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_classic_write_initial_success_on_exit(void* context) {
NfcApp* instance = context;
// Clear view
popup_reset(instance->popup);
}

View File

@@ -1,57 +0,0 @@
#include "../nfc_app_i.h"
void nfc_scene_mf_classic_write_initial_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_classic_write_initial_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, "Use The Source Card!");
widget_add_string_multiline_element(
widget,
4,
17,
AlignLeft,
AlignTop,
FontSecondary,
"Go to NFC Magic\napp if you want to\nwrite blanks");
widget_add_button_element(
widget,
GuiButtonTypeLeft,
"Retry",
nfc_scene_mf_classic_write_initial_wrong_card_widget_callback,
instance);
// Setup and start worker
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mf_classic_write_initial_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_classic_write_initial_wrong_card_on_exit(void* context) {
NfcApp* instance = context;
widget_reset(instance->widget);
}

View File

@@ -1,120 +0,0 @@
#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_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter);
popup_set_text(
instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, 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

@@ -1,67 +0,0 @@
#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

@@ -1,46 +0,0 @@
#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) {
bool was_saved =
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);
consumed = scene_manager_search_and_switch_to_previous_scene(
instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess);
}
}
return consumed;
}
void nfc_scene_mf_ultralight_write_success_on_exit(void* context) {
NfcApp* instance = context;
// Clear view
popup_reset(instance->popup);
}

View File

@@ -1,58 +0,0 @@
#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

@@ -1,6 +1,12 @@
#include "../helpers/protocol_support/nfc_protocol_support.h" #include "../helpers/protocol_support/nfc_protocol_support.h"
#include "../nfc_app_i.h"
void nfc_scene_read_on_enter(void* context) { void nfc_scene_read_on_enter(void* context) {
NfcApp* instance = context;
nfc_show_loading_popup(instance, true);
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
nfc_show_loading_popup(instance, false);
nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context); nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context);
} }

View File

@@ -56,37 +56,25 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect);
dolphin_deed(DolphinDeedNfcRead); dolphin_deed(DolphinDeedNfcRead);
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) { } else if(event.event == SubmenuIndexDetectReader) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader);
consumed = true;
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
// Save the scene state explicitly in each branch, so that
// if the user cancels loading a file, the Saved menu item
// is properly reselected.
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexExtraAction) { } else if(event.event == SubmenuIndexExtraAction) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction);
scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);
consumed = true;
} else if(event.event == SubmenuIndexAddManually) { } else if(event.event == SubmenuIndexAddManually) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
consumed = true;
} else if(event.event == SubmenuIndexDebug) { } else if(event.event == SubmenuIndexDebug) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
consumed = true; } else {
consumed = false;
}
if(consumed) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event);
} }
} }
return consumed; return consumed;

View File

@@ -0,0 +1,13 @@
#include "../helpers/protocol_support/nfc_protocol_support.h"
void nfc_scene_write_on_enter(void* context) {
nfc_protocol_support_on_enter(NfcProtocolSupportSceneWrite, context);
}
bool nfc_scene_write_on_event(void* context, SceneManagerEvent event) {
return nfc_protocol_support_on_event(NfcProtocolSupportSceneWrite, context, event);
}
void nfc_scene_write_on_exit(void* context) {
nfc_protocol_support_on_exit(NfcProtocolSupportSceneWrite, context);
}

View File

@@ -7,6 +7,7 @@
struct Submenu { struct Submenu {
View* view; View* view;
FuriTimer* locked_timer; FuriTimer* locked_timer;
}; };
@@ -19,6 +20,7 @@ typedef struct {
}; };
void* callback_context; void* callback_context;
bool has_extended_events; bool has_extended_events;
bool locked; bool locked;
FuriString* locked_message; FuriString* locked_message;
} SubmenuItem; } SubmenuItem;
@@ -68,6 +70,7 @@ typedef struct {
FuriString* header; FuriString* header;
size_t position; size_t position;
size_t window_position; size_t window_position;
bool locked_message_visible; bool locked_message_visible;
bool is_vertical; bool is_vertical;
} SubmenuModel; } SubmenuModel;
@@ -76,9 +79,9 @@ static void submenu_process_up(Submenu* submenu);
static void submenu_process_down(Submenu* submenu); static void submenu_process_down(Submenu* submenu);
static void submenu_process_ok(Submenu* submenu, InputType input_type); static void submenu_process_ok(Submenu* submenu, InputType input_type);
static size_t submenu_items_on_screen(bool header, bool vertical) { static size_t submenu_items_on_screen(SubmenuModel* model) {
size_t res = (vertical) ? 8 : 4; size_t res = (model->is_vertical) ? 8 : 4;
return (header) ? res - 1 : res; return (furi_string_empty(model->header)) ? res : res - 1;
} }
static void submenu_view_draw_callback(Canvas* canvas, void* _model) { static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
@@ -101,9 +104,9 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
SubmenuItemArray_next(it)) { SubmenuItemArray_next(it)) {
const size_t item_position = position - model->window_position; const size_t item_position = position - model->window_position;
const size_t items_on_screen = const size_t items_on_screen = submenu_items_on_screen(model);
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height;
uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16; bool is_locked = SubmenuItemArray_cref(it)->locked;
if(item_position < items_on_screen) { if(item_position < items_on_screen) {
if(position == model->position) { if(position == model->position) {
@@ -119,7 +122,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
} }
if(SubmenuItemArray_cref(it)->locked) { if(is_locked) {
canvas_draw_icon( canvas_draw_icon(
canvas, canvas,
item_width - 10, item_width - 10,
@@ -127,10 +130,8 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
&I_Lock_7x8); &I_Lock_7x8);
} }
FuriString* disp_str; FuriString* disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label); elements_string_fit_width(canvas, disp_str, item_width - (is_locked ? 21 : 11));
elements_string_fit_width(
canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 21 : 11));
canvas_draw_str( canvas_draw_str(
canvas, canvas,
@@ -161,25 +162,14 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 3); canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 3);
canvas_draw_rframe(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2, 2); canvas_draw_rframe(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2, 2);
if(model->is_vertical) { elements_multiline_text_aligned(
elements_multiline_text_aligned( canvas,
canvas, (model->is_vertical) ? 32 : 84,
32, (model->is_vertical) ? 42 : 32,
42, AlignCenter,
AlignCenter, AlignCenter,
AlignCenter, furi_string_get_cstr(
furi_string_get_cstr( SubmenuItemArray_get(model->items, model->position)->locked_message));
SubmenuItemArray_get(model->items, model->position)->locked_message));
} else {
elements_multiline_text_aligned(
canvas,
84,
32,
AlignCenter,
AlignCenter,
furi_string_get_cstr(
SubmenuItemArray_get(model->items, model->position)->locked_message));
}
} }
} }
@@ -195,8 +185,7 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
{ locked_message_visible = model->locked_message_visible; }, { locked_message_visible = model->locked_message_visible; },
false); false);
if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && if(locked_message_visible && (event->type == InputTypeShort || event->type == InputTypeLong)) {
locked_message_visible) {
with_view_model( with_view_model(
submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true); submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true);
consumed = true; consumed = true;
@@ -303,6 +292,9 @@ void submenu_add_lockable_item(
SubmenuItem* item = NULL; SubmenuItem* item = NULL;
furi_check(label); furi_check(label);
furi_check(submenu); furi_check(submenu);
if(locked) {
furi_check(locked_message);
}
with_view_model( with_view_model(
submenu->view, submenu->view,
@@ -366,6 +358,25 @@ void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* lab
true); true);
} }
void submenu_remove_item(Submenu* submenu, uint32_t index) {
furi_check(submenu);
with_view_model(
submenu->view,
SubmenuModel * model,
{
SubmenuItemArray_it_t it;
for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
SubmenuItemArray_next(it)) {
if(index == SubmenuItemArray_cref(it)->index) {
SubmenuItemArray_remove(model->items, it);
break;
}
}
},
true);
}
void submenu_reset(Submenu* submenu) { void submenu_reset(Submenu* submenu) {
furi_check(submenu); furi_check(submenu);
view_set_orientation(submenu->view, ViewOrientationHorizontal); view_set_orientation(submenu->view, ViewOrientationHorizontal);
@@ -431,8 +442,7 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) {
model->window_position -= 1; model->window_position -= 1;
} }
const size_t items_on_screen = const size_t items_on_screen = submenu_items_on_screen(model);
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
if(items_size <= items_on_screen) { if(items_size <= items_on_screen) {
model->window_position = 0; model->window_position = 0;
@@ -451,8 +461,7 @@ void submenu_process_up(Submenu* submenu) {
submenu->view, submenu->view,
SubmenuModel * model, SubmenuModel * model,
{ {
const size_t items_on_screen = const size_t items_on_screen = submenu_items_on_screen(model);
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
const size_t items_size = SubmenuItemArray_size(model->items); const size_t items_size = SubmenuItemArray_size(model->items);
if(model->position > 0) { if(model->position > 0) {
@@ -475,8 +484,7 @@ void submenu_process_down(Submenu* submenu) {
submenu->view, submenu->view,
SubmenuModel * model, SubmenuModel * model,
{ {
const size_t items_on_screen = const size_t items_on_screen = submenu_items_on_screen(model);
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
const size_t items_size = SubmenuItemArray_size(model->items); const size_t items_size = SubmenuItemArray_size(model->items);
if(model->position < items_size - 1) { if(model->position < items_size - 1) {
@@ -504,7 +512,8 @@ void submenu_process_ok(Submenu* submenu, InputType input_type) {
if(model->position < items_size) { if(model->position < items_size) {
item = SubmenuItemArray_get(model->items, model->position); item = SubmenuItemArray_get(model->items, model->position);
} }
if(item && item->locked) { if(item && item->locked &&
(input_type == InputTypeShort || input_type == InputTypeLong)) {
model->locked_message_visible = true; model->locked_message_visible = true;
furi_timer_start(submenu->locked_timer, furi_kernel_get_tick_frequency() * 3); furi_timer_start(submenu->locked_timer, furi_kernel_get_tick_frequency() * 3);
} }
@@ -540,11 +549,9 @@ void submenu_set_header(Submenu* submenu, const char* header) {
} }
void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) { void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) {
furi_assert(submenu); furi_check(submenu);
const bool is_vertical = const bool is_vertical = orientation == ViewOrientationVertical ||
(orientation == ViewOrientationVertical || orientation == ViewOrientationVerticalFlip) ? orientation == ViewOrientationVerticalFlip;
true :
false;
view_set_orientation(submenu->view, orientation); view_set_orientation(submenu->view, orientation);
@@ -558,8 +565,7 @@ void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) {
// Need if _set_orientation is called after _set_selected_item // Need if _set_orientation is called after _set_selected_item
size_t position = model->position; size_t position = model->position;
const size_t items_size = SubmenuItemArray_size(model->items); const size_t items_size = SubmenuItemArray_size(model->items);
const size_t items_on_screen = const size_t items_on_screen = submenu_items_on_screen(model);
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
if(position >= items_size) { if(position >= items_size) {
position = 0; position = 0;

View File

@@ -98,6 +98,14 @@ void submenu_add_item_ex(
*/ */
void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label); void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label);
/** Remove item from submenu
*
* @param submenu Submenu instance
* @param index menu item index, used for callback, may be
* the same with other items, first one is removed
*/
void submenu_remove_item(Submenu* submenu, uint32_t index);
/** Remove all items from submenu /** Remove all items from submenu
* *
* @param submenu Submenu instance * @param submenu Submenu instance
@@ -120,13 +128,14 @@ uint32_t submenu_get_selected_item(Submenu* submenu);
void submenu_set_selected_item(Submenu* submenu, uint32_t index); void submenu_set_selected_item(Submenu* submenu, uint32_t index);
/** Set optional header for submenu /** Set optional header for submenu
* Must be called before adding items OR after adding items and before set_selected_item()
* *
* @param submenu Submenu instance * @param submenu Submenu instance
* @param header header to set * @param header header to set
*/ */
void submenu_set_header(Submenu* submenu, const char* header); void submenu_set_header(Submenu* submenu, const char* header);
/** Set Orientation /** Set submenu orientation
* *
* @param submenu Submenu instance * @param submenu Submenu instance
* @param orientation either vertical or horizontal * @param orientation either vertical or horizontal

View File

@@ -19,39 +19,48 @@ env.Append(
File("protocols/iso14443_3b/iso14443_3b.h"), File("protocols/iso14443_3b/iso14443_3b.h"),
File("protocols/iso14443_4a/iso14443_4a.h"), File("protocols/iso14443_4a/iso14443_4a.h"),
File("protocols/iso14443_4b/iso14443_4b.h"), File("protocols/iso14443_4b/iso14443_4b.h"),
File("protocols/iso15693_3/iso15693_3.h"),
File("protocols/felica/felica.h"),
File("protocols/mf_ultralight/mf_ultralight.h"), File("protocols/mf_ultralight/mf_ultralight.h"),
File("protocols/mf_classic/mf_classic.h"), File("protocols/mf_classic/mf_classic.h"),
File("protocols/mf_plus/mf_plus.h"), File("protocols/mf_plus/mf_plus.h"),
File("protocols/mf_desfire/mf_desfire.h"), File("protocols/mf_desfire/mf_desfire.h"),
File("protocols/emv/emv.h"),
File("protocols/slix/slix.h"), File("protocols/slix/slix.h"),
File("protocols/st25tb/st25tb.h"), File("protocols/st25tb/st25tb.h"),
File("protocols/felica/felica.h"), File("protocols/ntag4xx/ntag4xx.h"),
File("protocols/type_4_tag/type_4_tag.h"),
File("protocols/emv/emv.h"),
# Pollers # Pollers
File("protocols/iso14443_3a/iso14443_3a_poller.h"), File("protocols/iso14443_3a/iso14443_3a_poller.h"),
File("protocols/iso14443_3b/iso14443_3b_poller.h"), File("protocols/iso14443_3b/iso14443_3b_poller.h"),
File("protocols/iso14443_4a/iso14443_4a_poller.h"), File("protocols/iso14443_4a/iso14443_4a_poller.h"),
File("protocols/iso14443_4b/iso14443_4b_poller.h"), File("protocols/iso14443_4b/iso14443_4b_poller.h"),
File("protocols/iso15693_3/iso15693_3_poller.h"),
File("protocols/felica/felica_poller.h"),
File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_ultralight/mf_ultralight_poller.h"),
File("protocols/mf_classic/mf_classic_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"),
File("protocols/mf_plus/mf_plus_poller.h"), File("protocols/mf_plus/mf_plus_poller.h"),
File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"),
File("protocols/slix/slix_poller.h"), File("protocols/slix/slix_poller.h"),
File("protocols/emv/emv_poller.h"),
File("protocols/st25tb/st25tb_poller.h"), File("protocols/st25tb/st25tb_poller.h"),
File("protocols/felica/felica_poller.h"), File("protocols/ntag4xx/ntag4xx_poller.h"),
File("protocols/type_4_tag/type_4_tag_poller.h"),
File("protocols/emv/emv_poller.h"),
# Listeners # Listeners
File("protocols/iso14443_3a/iso14443_3a_listener.h"), File("protocols/iso14443_3a/iso14443_3a_listener.h"),
File("protocols/iso14443_4a/iso14443_4a_listener.h"), File("protocols/iso14443_4a/iso14443_4a_listener.h"),
File("protocols/iso15693_3/iso15693_3_listener.h"),
File("protocols/felica/felica_listener.h"),
File("protocols/mf_ultralight/mf_ultralight_listener.h"), File("protocols/mf_ultralight/mf_ultralight_listener.h"),
File("protocols/mf_classic/mf_classic_listener.h"), File("protocols/mf_classic/mf_classic_listener.h"),
File("protocols/felica/felica_listener.h"), File("protocols/slix/slix_listener.h"),
File("protocols/type_4_tag/type_4_tag_listener.h"),
# Sync API # Sync API
File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"), File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"),
File("protocols/felica/felica_poller_sync.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"), File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"),
File("protocols/mf_classic/mf_classic_poller_sync.h"), File("protocols/mf_classic/mf_classic_poller_sync.h"),
File("protocols/st25tb/st25tb_poller_sync.h"), File("protocols/st25tb/st25tb_poller_sync.h"),
File("protocols/felica/felica_poller_sync.h"),
# Misc # Misc
File("helpers/nfc_util.h"), File("helpers/nfc_util.h"),
File("helpers/iso14443_crc.h"), File("helpers/iso14443_crc.h"),

View File

@@ -14,6 +14,8 @@
#define ISO14443_4_BLOCK_PCB_MASK (0x03) #define ISO14443_4_BLOCK_PCB_MASK (0x03)
#define ISO14443_4_BLOCK_PCB_I (0U) #define ISO14443_4_BLOCK_PCB_I (0U)
#define ISO14443_4_BLOCK_PCB_I_MASK (1U << 1)
#define ISO14443_4_BLOCK_PCB_I_ZERO_MASK (7U << 5)
#define ISO14443_4_BLOCK_PCB_I_NAD_OFFSET (2) #define ISO14443_4_BLOCK_PCB_I_NAD_OFFSET (2)
#define ISO14443_4_BLOCK_PCB_I_CID_OFFSET (3) #define ISO14443_4_BLOCK_PCB_I_CID_OFFSET (3)
#define ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET (4) #define ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET (4)
@@ -33,8 +35,14 @@
#define ISO14443_4_BLOCK_PCB_S_CID_MASK (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET) #define ISO14443_4_BLOCK_PCB_S_CID_MASK (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET)
#define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK (3U << ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET) #define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK (3U << ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET)
#define ISO14443_4_BLOCK_CID_MASK (0x0F)
#define ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, mask) (((pcb) & (mask)) == (mask)) #define ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, mask) (((pcb) & (mask)) == (mask))
#define ISO14443_4_BLOCK_PCB_IS_I_BLOCK(pcb) \
(ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_I_MASK) && \
(((pcb) & ISO14443_4_BLOCK_PCB_I_ZERO_MASK) == 0))
#define ISO14443_4_BLOCK_PCB_IS_R_BLOCK(pcb) \ #define ISO14443_4_BLOCK_PCB_IS_R_BLOCK(pcb) \
ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_MASK) ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_MASK)
@@ -47,14 +55,23 @@
#define ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(pcb) \ #define ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(pcb) \
ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_NACK_MASK) ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_NACK_MASK)
#define ISO14443_4_LAYER_NAD_NOT_SUPPORTED ((uint8_t) - 1)
#define ISO14443_4_LAYER_NAD_NOT_SET ((uint8_t) - 2)
struct Iso14443_4Layer { struct Iso14443_4Layer {
uint8_t pcb; uint8_t pcb;
uint8_t pcb_prev; uint8_t pcb_prev;
// Listener specific
uint8_t cid;
uint8_t nad;
}; };
static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance) { static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance, bool toggle_num) {
instance->pcb_prev = instance->pcb; instance->pcb_prev = instance->pcb;
instance->pcb ^= (uint8_t)0x01; if(toggle_num) {
instance->pcb ^= (uint8_t)0x01;
}
} }
Iso14443_4Layer* iso14443_4_layer_alloc(void) { Iso14443_4Layer* iso14443_4_layer_alloc(void) {
@@ -73,6 +90,9 @@ void iso14443_4_layer_reset(Iso14443_4Layer* instance) {
furi_assert(instance); furi_assert(instance);
instance->pcb_prev = 0; instance->pcb_prev = 0;
instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB; instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB;
instance->cid = ISO14443_4_LAYER_CID_NOT_SUPPORTED;
instance->nad = ISO14443_4_LAYER_NAD_NOT_SUPPORTED;
} }
void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present) { void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present) {
@@ -96,7 +116,7 @@ void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool
(CID_present << ISO14443_4_BLOCK_PCB_S_CID_OFFSET) | ISO14443_4_BLOCK_PCB; (CID_present << ISO14443_4_BLOCK_PCB_S_CID_OFFSET) | ISO14443_4_BLOCK_PCB;
} }
void iso14443_4_layer_encode_block( void iso14443_4_layer_encode_command(
Iso14443_4Layer* instance, Iso14443_4Layer* instance,
const BitBuffer* input_data, const BitBuffer* input_data,
BitBuffer* block_data) { BitBuffer* block_data) {
@@ -105,7 +125,7 @@ void iso14443_4_layer_encode_block(
bit_buffer_append_byte(block_data, instance->pcb); bit_buffer_append_byte(block_data, instance->pcb);
bit_buffer_append(block_data, input_data); bit_buffer_append(block_data, input_data);
iso14443_4_layer_update_pcb(instance); iso14443_4_layer_update_pcb(instance, true);
} }
static inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_data) { static inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_data) {
@@ -113,7 +133,7 @@ static inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_d
return data[0]; return data[0];
} }
bool iso14443_4_layer_decode_block( bool iso14443_4_layer_decode_response(
Iso14443_4Layer* instance, Iso14443_4Layer* instance,
BitBuffer* output_data, BitBuffer* output_data,
const BitBuffer* block_data) { const BitBuffer* block_data) {
@@ -127,7 +147,7 @@ bool iso14443_4_layer_decode_block(
ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) && ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) &&
(!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb)); (!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb));
instance->pcb &= ISO14443_4_BLOCK_PCB_MASK; instance->pcb &= ISO14443_4_BLOCK_PCB_MASK;
iso14443_4_layer_update_pcb(instance); iso14443_4_layer_update_pcb(instance, true);
} else if(ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(instance->pcb_prev)) { } else if(ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(instance->pcb_prev)) {
const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data); const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data);
ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) && ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) &&
@@ -147,7 +167,7 @@ bool iso14443_4_layer_decode_block(
return ret; return ret;
} }
Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( Iso14443_4aError iso14443_4_layer_decode_response_pwt_ext(
Iso14443_4Layer* instance, Iso14443_4Layer* instance,
BitBuffer* output_data, BitBuffer* output_data,
const BitBuffer* block_data) { const BitBuffer* block_data) {
@@ -199,3 +219,100 @@ Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext(
return ret; return ret;
} }
void iso14443_4_layer_set_cid(Iso14443_4Layer* instance, uint8_t cid) {
instance->cid = cid;
}
void iso14443_4_layer_set_nad_supported(Iso14443_4Layer* instance, bool nad) {
instance->nad = nad ? 0 : ISO14443_4_LAYER_NAD_NOT_SUPPORTED;
}
Iso14443_4LayerResult iso14443_4_layer_decode_command(
Iso14443_4Layer* instance,
const BitBuffer* input_data,
BitBuffer* block_data) {
furi_assert(instance);
uint8_t prologue_len = 0;
instance->pcb = bit_buffer_get_byte(input_data, prologue_len++);
if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb)) {
if(instance->pcb & ISO14443_4_BLOCK_PCB_I_CID_MASK) {
const uint8_t cid = bit_buffer_get_byte(input_data, prologue_len++) &
ISO14443_4_BLOCK_CID_MASK;
if(instance->cid == ISO14443_4_LAYER_CID_NOT_SUPPORTED || cid != instance->cid) {
return Iso14443_4LayerResultSkip;
}
} else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) {
return Iso14443_4LayerResultSkip;
}
// TODO: properly handle block chaining
if(instance->pcb & ISO14443_4_BLOCK_PCB_I_NAD_MASK) {
if(instance->nad == ISO14443_4_LAYER_NAD_NOT_SUPPORTED) {
return Iso14443_4LayerResultSkip;
}
instance->nad = bit_buffer_get_byte(input_data, prologue_len++);
}
bit_buffer_copy_right(block_data, input_data, prologue_len);
iso14443_4_layer_update_pcb(instance, false);
return Iso14443_4LayerResultData;
} else if(ISO14443_4_BLOCK_PCB_IS_S_BLOCK(instance->pcb)) {
if(instance->pcb & ISO14443_4_BLOCK_PCB_S_CID_MASK) {
const uint8_t cid = bit_buffer_get_byte(input_data, prologue_len++) &
ISO14443_4_BLOCK_CID_MASK;
if(instance->cid == ISO14443_4_LAYER_CID_NOT_SUPPORTED || cid != instance->cid) {
return Iso14443_4LayerResultSkip;
}
} else if(instance->cid != ISO14443_4_LAYER_CID_NOT_SUPPORTED && instance->cid != 0) {
return Iso14443_4LayerResultSkip;
}
if((instance->pcb & ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK) == 0) {
// DESELECT
bit_buffer_copy(block_data, input_data);
return Iso14443_4LayerResultSend | Iso14443_4LayerResultHalt;
} else {
// WTX ACK or wrong value
return Iso14443_4LayerResultSkip;
}
} else if(ISO14443_4_BLOCK_PCB_IS_R_BLOCK(instance->pcb)) {
// TODO: properly handle R blocks while chaining
iso14443_4_layer_update_pcb(instance, true);
instance->pcb |= ISO14443_4_BLOCK_PCB_R_NACK_MASK;
bit_buffer_reset(block_data);
bit_buffer_append_byte(block_data, instance->pcb);
iso14443_4_layer_update_pcb(instance, false);
return Iso14443_4LayerResultSend;
}
return Iso14443_4LayerResultSkip;
}
bool iso14443_4_layer_encode_response(
Iso14443_4Layer* instance,
const BitBuffer* input_data,
BitBuffer* block_data) {
furi_assert(instance);
if(ISO14443_4_BLOCK_PCB_IS_I_BLOCK(instance->pcb_prev)) {
bit_buffer_append_byte(block_data, 0x00);
if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_CID_MASK) {
bit_buffer_append_byte(block_data, instance->cid);
}
// TODO: properly handle block chaining and related R block responses
if(instance->pcb_prev & ISO14443_4_BLOCK_PCB_I_NAD_MASK &&
instance->nad != ISO14443_4_LAYER_NAD_NOT_SET) {
bit_buffer_append_byte(block_data, instance->nad);
instance->nad = ISO14443_4_LAYER_NAD_NOT_SET;
} else {
instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_NAD_MASK;
}
instance->pcb &= ~ISO14443_4_BLOCK_PCB_I_CHAIN_MASK;
bit_buffer_set_byte(block_data, 0, instance->pcb);
bit_buffer_append(block_data, input_data);
iso14443_4_layer_update_pcb(instance, false);
return true;
}
return false;
}

View File

@@ -19,21 +19,47 @@ void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool
void iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present); void iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present);
void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present); void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present);
void iso14443_4_layer_encode_block( // Poller mode
void iso14443_4_layer_encode_command(
Iso14443_4Layer* instance, Iso14443_4Layer* instance,
const BitBuffer* input_data, const BitBuffer* input_data,
BitBuffer* block_data); BitBuffer* block_data);
bool iso14443_4_layer_decode_block( bool iso14443_4_layer_decode_response(
Iso14443_4Layer* instance, Iso14443_4Layer* instance,
BitBuffer* output_data, BitBuffer* output_data,
const BitBuffer* block_data); const BitBuffer* block_data);
Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext( Iso14443_4aError iso14443_4_layer_decode_response_pwt_ext(
Iso14443_4Layer* instance, Iso14443_4Layer* instance,
BitBuffer* output_data, BitBuffer* output_data,
const BitBuffer* block_data); const BitBuffer* block_data);
// Listener mode
typedef enum {
Iso14443_4LayerResultSkip = (0),
Iso14443_4LayerResultData = (1 << 1),
Iso14443_4LayerResultSend = (1 << 2),
Iso14443_4LayerResultHalt = (1 << 3),
} Iso14443_4LayerResult;
Iso14443_4LayerResult iso14443_4_layer_decode_command(
Iso14443_4Layer* instance,
const BitBuffer* input_data,
BitBuffer* block_data);
bool iso14443_4_layer_encode_response(
Iso14443_4Layer* instance,
const BitBuffer* input_data,
BitBuffer* block_data);
#define ISO14443_4_LAYER_CID_NOT_SUPPORTED ((uint8_t) - 1)
void iso14443_4_layer_set_cid(Iso14443_4Layer* instance, uint8_t cid);
void iso14443_4_layer_set_nad_supported(Iso14443_4Layer* instance, bool nad);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -0,0 +1,115 @@
#include "nxp_native_command.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
#define TAG "NxpNativeCommand"
Iso14443_4aError nxp_native_command_iso14443_4a_poller(
Iso14443_4aPoller* iso14443_4a_poller,
NxpNativeCommandStatus* status_code,
const BitBuffer* input_buffer,
BitBuffer* result_buffer,
NxpNativeCommandMode command_mode,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
furi_check(iso14443_4a_poller);
furi_check(tx_buffer);
furi_check(rx_buffer);
furi_check(input_buffer);
furi_check(result_buffer);
furi_check(command_mode < NxpNativeCommandModeMAX);
Iso14443_4aError error = Iso14443_4aErrorNone;
*status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK;
do {
bit_buffer_reset(tx_buffer);
if(command_mode == NxpNativeCommandModePlain) {
bit_buffer_append(tx_buffer, input_buffer);
} else if(command_mode == NxpNativeCommandModeIsoWrapped) {
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA);
bit_buffer_append_byte(tx_buffer, bit_buffer_get_byte(input_buffer, 0));
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1);
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2);
if(bit_buffer_get_size_bytes(input_buffer) > 1) {
bit_buffer_append_byte(tx_buffer, bit_buffer_get_size_bytes(input_buffer) - 1);
bit_buffer_append_right(tx_buffer, input_buffer, 1);
}
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE);
}
bit_buffer_reset(rx_buffer);
error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
if(error != Iso14443_4aErrorNone) {
break;
}
bit_buffer_reset(tx_buffer);
if(command_mode == NxpNativeCommandModePlain) {
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME);
} else if(command_mode == NxpNativeCommandModeIsoWrapped) {
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA);
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME);
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1);
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2);
bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE);
}
size_t response_len = bit_buffer_get_size_bytes(rx_buffer);
*status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR;
bit_buffer_reset(result_buffer);
if(command_mode == NxpNativeCommandModePlain && response_len >= sizeof(uint8_t)) {
*status_code = bit_buffer_get_byte(rx_buffer, 0);
if(response_len > sizeof(uint8_t)) {
bit_buffer_copy_right(result_buffer, rx_buffer, sizeof(uint8_t));
}
} else if(
command_mode == NxpNativeCommandModeIsoWrapped &&
response_len >= 2 * sizeof(uint8_t) &&
bit_buffer_get_byte(rx_buffer, response_len - 2) == NXP_NATIVE_COMMAND_ISO_SW1) {
*status_code = bit_buffer_get_byte(rx_buffer, response_len - 1);
if(response_len > 2 * sizeof(uint8_t)) {
bit_buffer_copy_left(result_buffer, rx_buffer, response_len - 2 * sizeof(uint8_t));
}
}
while(*status_code == NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME) {
bit_buffer_reset(rx_buffer);
error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer);
if(error != Iso14443_4aErrorNone) {
break;
}
const size_t rx_size = bit_buffer_get_size_bytes(rx_buffer);
const size_t rx_capacity_remaining = bit_buffer_get_capacity_bytes(result_buffer) -
bit_buffer_get_size_bytes(result_buffer);
if(command_mode == NxpNativeCommandModePlain) {
*status_code = rx_size >= 1 ? bit_buffer_get_byte(rx_buffer, 0) :
NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR;
if(rx_size <= rx_capacity_remaining + 1) {
bit_buffer_append_right(result_buffer, rx_buffer, sizeof(uint8_t));
} else {
FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1);
}
} else if(command_mode == NxpNativeCommandModeIsoWrapped) {
if(rx_size >= 2 &&
bit_buffer_get_byte(rx_buffer, rx_size - 2) == NXP_NATIVE_COMMAND_ISO_SW1) {
*status_code = bit_buffer_get_byte(rx_buffer, rx_size - 1);
} else {
*status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR;
}
if(rx_size <= rx_capacity_remaining + 2) {
bit_buffer_set_size_bytes(rx_buffer, rx_size - 2);
bit_buffer_append(result_buffer, rx_buffer);
} else {
FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2);
}
}
}
} while(false);
return error;
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include "nxp_native_command_mode.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
// ISO 7816 command wrapping
#define NXP_NATIVE_COMMAND_ISO_CLA (0x90)
#define NXP_NATIVE_COMMAND_ISO_P1 (0x00)
#define NXP_NATIVE_COMMAND_ISO_P2 (0x00)
#define NXP_NATIVE_COMMAND_ISO_LE (0x00)
// ISO 7816 status wrapping
#define NXP_NATIVE_COMMAND_ISO_SW1 (0x91)
// Successful operation
#define NXP_NATIVE_COMMAND_STATUS_OPERATION_OK (0x00)
// No changes done to backup files, CommitTransaction / AbortTransaction not necessary
#define NXP_NATIVE_COMMAND_STATUS_NO_CHANGES (0x0C)
// Insufficient NV-Memory to complete command
#define NXP_NATIVE_COMMAND_STATUS_OUT_OF_EEPROM_ERROR (0x0E)
// Command code not supported
#define NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE (0x1C)
// CRC or MAC does not match data Padding bytes not valid
#define NXP_NATIVE_COMMAND_STATUS_INTEGRITY_ERROR (0x1E)
// Invalid key number specified
#define NXP_NATIVE_COMMAND_STATUS_NO_SUCH_KEY (0x40)
// Length of command string invalid
#define NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR (0x7E)
// Current configuration / status does not allow the requested command
#define NXP_NATIVE_COMMAND_STATUS_PERMISSION_DENIED (0x9D)
// Value of the parameter(s) invalid
#define NXP_NATIVE_COMMAND_STATUS_PARAMETER_ERROR (0x9E)
// Requested AID not present on PICC
#define NXP_NATIVE_COMMAND_STATUS_APPLICATION_NOT_FOUND (0xA0)
// Unrecoverable error within application, application will be disabled
#define NXP_NATIVE_COMMAND_STATUS_APPL_INTEGRITY_ERROR (0xA1)
// Currently not allowed to authenticate. Keep trying until full delay is spent
#define NXP_NATIVE_COMMAND_STATUS_STATUS_AUTHENTICATION_DELAY (0xAD)
// Current authentication status does not allow the requested command
#define NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR (0xAE)
// Additional data frame is expected to be sent
#define NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME (0xAF)
// Attempt to read/write data from/to beyond the file's/record's limits
// Attempt to exceed the limits of a value file.
#define NXP_NATIVE_COMMAND_STATUS_BOUNDARY_ERROR (0xBE)
// Unrecoverable error within PICC, PICC will be disabled
#define NXP_NATIVE_COMMAND_STATUS_PICC_INTEGRITY_ERROR (0xC1)
// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD
#define NXP_NATIVE_COMMAND_STATUS_COMMAND_ABORTED (0xCA)
// PICC was disabled by an unrecoverable error
#define NXP_NATIVE_COMMAND_STATUS_PICC_DISABLED_ERROR (0xCD)
// Number of Applications limited to 28, no additional CreateApplication possible
#define NXP_NATIVE_COMMAND_STATUS_COUNT_ERROR (0xCE)
// Creation of file/application failed because file/application with same number already exists
#define NXP_NATIVE_COMMAND_STATUS_DUBLICATE_ERROR (0xDE)
// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated
#define NXP_NATIVE_COMMAND_STATUS_EEPROM_ERROR (0xEE)
// Specified file number does not exist
#define NXP_NATIVE_COMMAND_STATUS_FILE_NOT_FOUND (0xF0)
// Unrecoverable error within file, file will be disabled
#define NXP_NATIVE_COMMAND_STATUS_FILE_INTEGRITY_ERROR (0xF1)
typedef uint8_t NxpNativeCommandStatus;
/**
* @brief Transmit and receive NXP Native Command chunks in poller mode.
*
* Must ONLY be used inside the callback function.
*
* The result_buffer will be filled with any data received as a response to data
* sent from input_buffer, with a timeout defined by the fwt parameter.
*
* The tx_buffer and rx_buffer are used as working areas to handle individual
* command chunks and responses.
*
* @param[in, out] iso14443_4a_poller pointer to the instance to be used in the transaction.
* @param[out] status_code pointer to a status variable to hold the result of the operation.
* @param[in] input_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] result_buffer pointer to the buffer to be filled with received data.
* @param[in] command_mode what command formatting mode to use for the transaction.
* @param[in, out] tx_buffer pointer to the buffer for command construction.
* @param[in, out] rx_buffer pointer to the buffer for response handling.
* @return Iso14443_4aErrorNone and STATUS_OPERATION_OK on success, an error code on failure.
*/
Iso14443_4aError nxp_native_command_iso14443_4a_poller(
Iso14443_4aPoller* iso14443_4a_poller,
NxpNativeCommandStatus* status_code,
const BitBuffer* input_buffer,
BitBuffer* result_buffer,
NxpNativeCommandMode command_mode,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer);

View File

@@ -0,0 +1,11 @@
#pragma once
/**
* @brief Enumeration of possible command modes.
*/
typedef enum {
NxpNativeCommandModePlain, /**< Plain native commands. */
NxpNativeCommandModeIsoWrapped, /**< ISO 7816-wrapped commands. */
NxpNativeCommandModeMAX,
} NxpNativeCommandMode;

View File

@@ -646,33 +646,6 @@ NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
return ret; return ret;
} }
NfcError nfc_iso15693_detect_mode(Nfc* instance) {
furi_check(instance);
FuriHalNfcError error = furi_hal_nfc_iso15693_detect_mode();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_iso15693_force_1outof4(Nfc* instance) {
furi_check(instance);
FuriHalNfcError error = furi_hal_nfc_iso15693_force_1outof4();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_iso15693_force_1outof256(Nfc* instance) {
furi_check(instance);
FuriHalNfcError error = furi_hal_nfc_iso15693_force_1outof256();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_felica_listener_set_sensf_res_data( NfcError nfc_felica_listener_set_sensf_res_data(
Nfc* instance, Nfc* instance,
const uint8_t* idm, const uint8_t* idm,

View File

@@ -380,30 +380,6 @@ NfcError nfc_felica_listener_set_sensf_res_data(
*/ */
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance); NfcError nfc_iso15693_listener_tx_sof(Nfc* instance);
/**
* @brief Set ISO15693 parser mode to autodetect
*
* @param[in,out] instance pointer to the instance to be configured.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_detect_mode(Nfc* instance);
/**
* @brief Set ISO15693 parser mode to 1OutOf4, disables autodetection
*
* @param[in,out] instance pointer to the instance to be configured.
* @return NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_force_1outof4(Nfc* instance);
/**
* @brief Set ISO15693 parser mode to 1OutOf256, disables autodetection
*
* @param[in,out] instance pointer to the instance to be configured.
* @return NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_force_1outof256(Nfc* instance);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -26,6 +26,47 @@ typedef struct {
Iso14443_3aListenerEventData* data; Iso14443_3aListenerEventData* data;
} Iso14443_3aListenerEvent; } Iso14443_3aListenerEvent;
/**
* @brief Transmit Iso14443_3a frames in listener mode.
*
* Must ONLY be used inside the callback function.
*
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @return Iso14443_3aErrorNone on success, an error code on failure.
*/
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer);
/**
* @brief Transmit Iso14443_3a frames with custom parity bits in listener mode.
*
* Must ONLY be used inside the callback function.
*
* Custom parity bits must be set in the tx_buffer.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @return Iso14443_3aErrorNone on success, an error code on failure.
*/
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
/**
* @brief Transmit Iso14443_3a standard frames in listener mode.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @return Iso14443_3aErrorNone on success, an error code on failure.
*/
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -26,17 +26,6 @@ struct Iso14443_3aListener {
void* context; void* context;
}; };
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -2,7 +2,8 @@
#include "iso14443_4a.h" #include "iso14443_4a.h"
#define ISO14443_4A_CMD_READ_ATS (0xE0) #define ISO14443_4A_CMD_READ_ATS (0xE0)
#define ISO14443_4A_READ_ATS_CID_MASK (0x0F)
// ATS bit definitions // ATS bit definitions
#define ISO14443_4A_ATS_T0_TA1 (1U << 4) #define ISO14443_4A_ATS_T0_TA1 (1U << 4)

View File

@@ -14,7 +14,9 @@ static Iso14443_4aListener*
Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener)); Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener));
instance->iso14443_3a_listener = iso14443_3a_listener; instance->iso14443_3a_listener = iso14443_3a_listener;
instance->data = data; instance->data = data;
instance->iso14443_4_layer = iso14443_4_layer_alloc();
instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE);
instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE); instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE);
instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data; instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;
@@ -27,13 +29,18 @@ static Iso14443_4aListener*
static void iso14443_4a_listener_free(Iso14443_4aListener* instance) { static void iso14443_4a_listener_free(Iso14443_4aListener* instance) {
furi_assert(instance); furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
iso14443_4_layer_free(instance->iso14443_4_layer);
bit_buffer_free(instance->rx_buffer);
bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->tx_buffer);
free(instance); free(instance);
} }
static void iso14443_4a_listener_reset(Iso14443_4aListener* instance) {
instance->state = Iso14443_4aListenerStateIdle;
iso14443_4_layer_reset(instance->iso14443_4_layer);
}
static void iso14443_4a_listener_set_callback( static void iso14443_4a_listener_set_callback(
Iso14443_4aListener* instance, Iso14443_4aListener* instance,
NfcGenericCallback callback, NfcGenericCallback callback,
@@ -68,20 +75,46 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context)
if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) == if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) ==
Iso14443_4aErrorNone) { Iso14443_4aErrorNone) {
instance->state = Iso14443_4aListenerStateActive; instance->state = Iso14443_4aListenerStateActive;
if(iso14443_4a_supports_frame_option(
instance->data, Iso14443_4aFrameOptionCid)) {
const uint8_t cid = bit_buffer_get_byte(rx_buffer, 1) &
ISO14443_4A_READ_ATS_CID_MASK;
iso14443_4_layer_set_cid(instance->iso14443_4_layer, cid);
}
iso14443_4_layer_set_nad_supported(
instance->iso14443_4_layer,
iso14443_4a_supports_frame_option(
instance->data, Iso14443_4aFrameOptionNad));
} }
} }
} else { } else {
instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; Iso14443_4LayerResult status = iso14443_4_layer_decode_command(
instance->iso14443_4a_event.data->buffer = rx_buffer; instance->iso14443_4_layer, rx_buffer, instance->rx_buffer);
if(status & Iso14443_4LayerResultSend) {
iso14443_3a_listener_send_standard_frame(
instance->iso14443_3a_listener, instance->rx_buffer);
}
if(status & Iso14443_4LayerResultHalt) {
iso14443_4a_listener_reset(instance);
if(instance->callback) {
instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeHalted;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
}
if(status & Iso14443_4LayerResultData) {
instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData;
instance->iso14443_4a_event.data->buffer = instance->rx_buffer;
if(instance->callback) { if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context); command = instance->callback(instance->generic_event, instance->context);
}
} }
} }
} else if( } else if(
iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted || iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted ||
iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) { iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) {
instance->state = Iso14443_4aListenerStateIdle; iso14443_4a_listener_reset(instance);
instance->iso14443_4a_event.type = iso14443_3a_event->type == instance->iso14443_4a_event.type = iso14443_3a_event->type ==
Iso14443_3aListenerEventTypeHalted ? Iso14443_3aListenerEventTypeHalted ?

View File

@@ -25,6 +25,18 @@ typedef struct {
Iso14443_4aListenerEventData* data; Iso14443_4aListenerEventData* data;
} Iso14443_4aListenerEvent; } Iso14443_4aListenerEvent;
/**
* @brief Transmit Iso14443_4a blocks in listener mode.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @return Iso14443_4aErrorNone on success, an error code on failure.
*/
Iso14443_4aError
iso14443_4a_listener_send_block(Iso14443_4aListener* instance, const BitBuffer* tx_buffer);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -30,3 +30,17 @@ Iso14443_4aError
instance->iso14443_3a_listener, instance->tx_buffer); instance->iso14443_3a_listener, instance->tx_buffer);
return iso14443_4a_process_error(error); return iso14443_4a_process_error(error);
} }
Iso14443_4aError
iso14443_4a_listener_send_block(Iso14443_4aListener* instance, const BitBuffer* tx_buffer) {
bit_buffer_reset(instance->tx_buffer);
if(!iso14443_4_layer_encode_response(
instance->iso14443_4_layer, tx_buffer, instance->tx_buffer)) {
return Iso14443_4aErrorProtocol;
}
const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame(
instance->iso14443_3a_listener, instance->tx_buffer);
return iso14443_4a_process_error(error);
}

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <nfc/protocols/nfc_generic_event.h> #include <nfc/protocols/nfc_generic_event.h>
#include <nfc/helpers/iso14443_4_layer.h>
#include "iso14443_4a_listener.h" #include "iso14443_4a_listener.h"
#include "iso14443_4a_i.h" #include "iso14443_4a_i.h"
@@ -17,8 +18,10 @@ typedef enum {
struct Iso14443_4aListener { struct Iso14443_4aListener {
Iso14443_3aListener* iso14443_3a_listener; Iso14443_3aListener* iso14443_3a_listener;
Iso14443_4aData* data; Iso14443_4aData* data;
Iso14443_4Layer* iso14443_4_layer;
Iso14443_4aListenerState state; Iso14443_4aListenerState state;
BitBuffer* rx_buffer;
BitBuffer* tx_buffer; BitBuffer* tx_buffer;
NfcGenericEvent generic_event; NfcGenericEvent generic_event;

View File

@@ -65,7 +65,7 @@ Iso14443_4aError iso14443_4a_poller_send_block(
furi_check(rx_buffer); furi_check(rx_buffer);
bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->tx_buffer);
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); iso14443_4_layer_encode_command(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
Iso14443_4aError error = Iso14443_4aErrorNone; Iso14443_4aError error = Iso14443_4aErrorNone;
@@ -106,7 +106,7 @@ Iso14443_4aError iso14443_4a_poller_send_block(
} while(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX)); } while(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX));
} }
if(!iso14443_4_layer_decode_block( if(!iso14443_4_layer_decode_response(
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) { instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
error = Iso14443_4aErrorProtocol; error = Iso14443_4aErrorProtocol;
break; break;
@@ -155,7 +155,7 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
uint8_t attempts_left = ISO14443_4A_SEND_BLOCK_MAX_ATTEMPTS; uint8_t attempts_left = ISO14443_4A_SEND_BLOCK_MAX_ATTEMPTS;
bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->tx_buffer);
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); iso14443_4_layer_encode_command(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
Iso14443_4aError error = Iso14443_4aErrorNone; Iso14443_4aError error = Iso14443_4aErrorNone;
@@ -180,7 +180,7 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
break; break;
} else { } else {
error = iso14443_4_layer_decode_block_pwt_ext( error = iso14443_4_layer_decode_response_pwt_ext(
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer); instance->iso14443_4_layer, rx_buffer, instance->rx_buffer);
if(error == Iso14443_4aErrorSendExtra) { if(error == Iso14443_4aErrorSendExtra) {
if(--attempts_left == 0) break; if(--attempts_left == 0) break;

View File

@@ -24,7 +24,7 @@ Iso14443_4bError iso14443_4b_poller_send_block(
furi_check(rx_buffer); furi_check(rx_buffer);
bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->tx_buffer);
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); iso14443_4_layer_encode_command(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
Iso14443_4bError error = Iso14443_4bErrorNone; Iso14443_4bError error = Iso14443_4bErrorNone;
@@ -36,7 +36,7 @@ Iso14443_4bError iso14443_4b_poller_send_block(
error = iso14443_4b_process_error(iso14443_3b_error); error = iso14443_4b_process_error(iso14443_3b_error);
break; break;
} else if(!iso14443_4_layer_decode_block( } else if(!iso14443_4_layer_decode_response(
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) { instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
error = Iso14443_4bErrorProtocol; error = Iso14443_4bErrorProtocol;
break; break;

View File

@@ -17,6 +17,13 @@ extern "C" {
#define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F) #define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F)
#define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5) #define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5)
#define MF_DESFIRE_CMD_CREATE_APPLICATION (0xCA)
#define MF_DESFIRE_CMD_CREATE_STD_DATA_FILE (0xCD)
#define MF_DESFIRE_CMD_CREATE_BACKUP_DATA_FILE (0xCB)
#define MF_DESFIRE_CMD_CREATE_VALUE_FILE (0xCC)
#define MF_DESFIRE_CMD_CREATE_LINEAR_RECORD_FILE (0xC1)
#define MF_DESFIRE_CMD_CREATE_CYCLIC_RECORD_FILE (0xC0)
#define MF_DESFIRE_CMD_READ_DATA (0xBD) #define MF_DESFIRE_CMD_READ_DATA (0xBD)
#define MF_DESFIRE_CMD_GET_VALUE (0x6C) #define MF_DESFIRE_CMD_GET_VALUE (0x6C)
#define MF_DESFIRE_CMD_READ_RECORDS (0xBB) #define MF_DESFIRE_CMD_READ_RECORDS (0xBB)

View File

@@ -60,7 +60,7 @@ bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) {
bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion)); bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion));
} }
return can_parse; return can_parse && (data->hw_type & 0x0F) == 0x01;
} }
bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) { bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) {
@@ -81,17 +81,17 @@ bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* bu
return can_parse; return can_parse;
} }
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) { typedef struct FURI_PACKED {
typedef struct FURI_PACKED { bool is_master_key_changeable : 1;
bool is_master_key_changeable : 1; bool is_free_directory_list : 1;
bool is_free_directory_list : 1; bool is_free_create_delete : 1;
bool is_free_create_delete : 1; bool is_config_changeable : 1;
bool is_config_changeable : 1; uint8_t change_key_id : 4;
uint8_t change_key_id : 4; uint8_t max_keys : 4;
uint8_t max_keys : 4; uint8_t flags : 4;
uint8_t flags : 4; } MfDesfireKeySettingsLayout;
} MfDesfireKeySettingsLayout;
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout); const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout);
if(can_parse) { if(can_parse) {
@@ -111,6 +111,21 @@ bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer*
return can_parse; return can_parse;
} }
void mf_desfire_key_settings_dump(const MfDesfireKeySettings* data, BitBuffer* buf) {
MfDesfireKeySettingsLayout layout;
layout.is_master_key_changeable = data->is_master_key_changeable;
layout.is_free_directory_list = data->is_free_directory_list;
layout.is_free_create_delete = data->is_free_create_delete;
layout.is_config_changeable = data->is_config_changeable;
layout.change_key_id = data->change_key_id;
layout.max_keys = data->max_keys;
layout.flags = data->flags;
bit_buffer_append_bytes(buf, (uint8_t*)&layout, sizeof(MfDesfireKeySettingsLayout));
}
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) { bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion); const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion);

View File

@@ -2,55 +2,11 @@
#include "mf_desfire.h" #include "mf_desfire.h"
#include <nfc/helpers/nxp_native_command.h>
#define MF_DESFIRE_FFF_PICC_PREFIX "PICC" #define MF_DESFIRE_FFF_PICC_PREFIX "PICC"
#define MF_DESFIRE_FFF_APP_PREFIX "Application" #define MF_DESFIRE_FFF_APP_PREFIX "Application"
// Successful operation
#define MF_DESFIRE_STATUS_OPERATION_OK (0x00)
// No changes done to backup files, CommitTransaction / AbortTransaction not necessary
#define MF_DESFIRE_STATUS_NO_CHANGES (0x0C)
// Insufficient NV-Memory to complete command
#define MF_DESFIRE_STATUS_OUT_OF_EEPROM_ERROR (0x0E)
// Command code not supported
#define MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE (0x1C)
// CRC or MAC does not match data Padding bytes not valid
#define MF_DESFIRE_STATUS_INTEGRITY_ERROR (0x1E)
// Invalid key number specified
#define MF_DESFIRE_STATUS_NO_SUCH_KEY (0x40)
// Length of command string invalid
#define MF_DESFIRE_STATUS_LENGTH_ERROR (0x7E)
// Current configuration / status does not allow the requested command
#define MF_DESFIRE_STATUS_PERMISSION_DENIED (0x9D)
// Value of the parameter(s) invalid
#define MF_DESFIRE_STATUS_PARAMETER_ERROR (0x9E)
// Requested AID not present on PICC
#define MF_DESFIRE_STATUS_APPLICATION_NOT_FOUND (0xA0)
// Unrecoverable error within application, application will be disabled
#define MF_DESFIRE_STATUS_APPL_INTEGRITY_ERROR (0xA1)
// Current authentication status does not allow the requested command
#define MF_DESFIRE_STATUS_AUTHENTICATION_ERROR (0xAE)
// Additional data frame is expected to be sent
#define MF_DESFIRE_STATUS_ADDITIONAL_FRAME (0xAF)
// Attempt to read/write data from/to beyond the file's/record's limits
// Attempt to exceed the limits of a value file.
#define MF_DESFIRE_STATUS_BOUNDARY_ERROR (0xBE)
// Unrecoverable error within PICC, PICC will be disabled
#define MF_DESFIRE_STATUS_PICC_INTEGRITY_ERROR (0xC1)
// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD
#define MF_DESFIRE_STATUS_COMMAND_ABORTED (0xCA)
// PICC was disabled by an unrecoverable error
#define MF_DESFIRE_STATUS_PICC_DISABLED_ERROR (0xCD)
// Number of Applications limited to 28, no additional CreateApplication possible
#define MF_DESFIRE_STATUS_COUNT_ERROR (0xCE)
// Creation of file/application failed because file/application with same number already exists
#define MF_DESFIRE_STATUS_DUBLICATE_ERROR (0xDE)
// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated
#define MF_DESFIRE_STATUS_EEPROM_ERROR (0xEE)
// Specified file number does not exist
#define MF_DESFIRE_STATUS_FILE_NOT_FOUND (0xF0)
// Unrecoverable error within file, file will be disabled
#define MF_DESFIRE_STATUS_FILE_INTEGRITY_ERROR (0xF1)
// SimpleArray configurations // SimpleArray configurations
extern const SimpleArrayConfig mf_desfire_key_version_array_config; extern const SimpleArrayConfig mf_desfire_key_version_array_config;
@@ -68,6 +24,8 @@ bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* bu
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf); bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf);
void mf_desfire_key_settings_dump(const MfDesfireKeySettings* data, BitBuffer* buf);
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf); bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf);
bool mf_desfire_application_id_parse( bool mf_desfire_application_id_parse(

View File

@@ -251,8 +251,7 @@ static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) {
MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version); MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
MfDesfireVersion version = {}; error = mf_desfire_poller_read_version(instance, &instance->data->version);
error = mf_desfire_poller_read_version(instance, &version);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
protocol_detected = true; protocol_detected = true;

View File

@@ -3,6 +3,7 @@
#include "mf_desfire.h" #include "mf_desfire.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h> #include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#include <lib/nfc/helpers/nxp_native_command_mode.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -38,6 +39,16 @@ typedef struct {
MfDesfirePollerEventData* data; /**< Pointer to event specific data. */ MfDesfirePollerEventData* data; /**< Pointer to event specific data. */
} MfDesfirePollerEvent; } MfDesfirePollerEvent;
/**
* @brief Change command mode used in poller mode.
*
* @param[in, out] instance pointer to the instance to affect.
* @param[in] command_mode command mode to use in further communication with the card.
*/
void mf_desfire_poller_set_command_mode(
MfDesfirePoller* instance,
NxpNativeCommandMode command_mode);
/** /**
* @brief Transmit and receive MfDesfire chunks in poller mode. * @brief Transmit and receive MfDesfire chunks in poller mode.
* *
@@ -51,11 +62,16 @@ typedef struct {
* @param[out] rx_buffer pointer to the buffer to be filled with received data. * @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @return MfDesfireErrorNone on success, an error code on failure. * @return MfDesfireErrorNone on success, an error code on failure.
*/ */
MfDesfireError mf_desfire_send_chunks( MfDesfireError mf_desfire_poller_send_chunks(
MfDesfirePoller* instance, MfDesfirePoller* instance,
const BitBuffer* tx_buffer, const BitBuffer* tx_buffer,
BitBuffer* rx_buffer); BitBuffer* rx_buffer);
/**
* @warning deprecated, use mf_desfire_poller_send_chunks instead
*/
#define mf_desfire_send_chunks mf_desfire_poller_send_chunks
/** /**
* @brief Read MfDesfire card version. * @brief Read MfDesfire card version.
* *
@@ -187,6 +203,44 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
const SimpleArray* file_ids, const SimpleArray* file_ids,
SimpleArray* data); SimpleArray* data);
/**
* @brief Create Application on MfDesfire card.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] id pointer to the application id for the new application.
* @param[in] key_settings pointer to the key settings for the new application.
* @param[in] iso_df_id optional iso identifier for the new application.
* @param[in] iso_df_name optional iso name for the new application.
* @param[in] iso_df_name_len length of the optional iso application name.
* @return MfDesfireErrorNone on success, an error code on failure.
*/
MfDesfireError mf_desfire_poller_create_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id,
const MfDesfireKeySettings* key_settings,
uint16_t iso_df_id,
const uint8_t* iso_df_name,
uint8_t iso_df_name_len);
/**
* @brief Create File on MfDesfire card.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] id file id for the new file.
* @param[in] data pointer to the file settings for the new file.
* @param[in] iso_ef_id optional iso identifier for the new file.
* @return MfDesfireErrorNone on success, an error code on failure.
*/
MfDesfireError mf_desfire_poller_create_file(
MfDesfirePoller* instance,
MfDesfireFileId id,
const MfDesfireFileSettings* data,
uint16_t iso_ef_id);
/** /**
* @brief Read file data on MfDesfire card. * @brief Read file data on MfDesfire card.
* *

View File

@@ -1,6 +1,7 @@
#include "mf_desfire_poller_i.h" #include "mf_desfire_poller_i.h"
#include <furi.h> #include <furi.h>
#include <bit_lib/bit_lib.h>
#include "mf_desfire_i.h" #include "mf_desfire_i.h"
@@ -21,76 +22,48 @@ MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
MfDesfireError mf_desfire_process_status_code(uint8_t status_code) { MfDesfireError mf_desfire_process_status_code(uint8_t status_code) {
switch(status_code) { switch(status_code) {
case MF_DESFIRE_STATUS_OPERATION_OK: case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK:
return MfDesfireErrorNone; return MfDesfireErrorNone;
case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR: case NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR:
return MfDesfireErrorAuthentication; return MfDesfireErrorAuthentication;
case MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE: case NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE:
return MfDesfireErrorCommandNotSupported; return MfDesfireErrorCommandNotSupported;
default: default:
return MfDesfireErrorProtocol; return MfDesfireErrorProtocol;
} }
} }
MfDesfireError mf_desfire_send_chunks( void mf_desfire_poller_set_command_mode(
MfDesfirePoller* instance,
NxpNativeCommandMode command_mode) {
furi_check(instance);
furi_check(instance->state == MfDesfirePollerStateIdle);
furi_check(command_mode < NxpNativeCommandModeMAX);
instance->command_mode = command_mode;
}
MfDesfireError mf_desfire_poller_send_chunks(
MfDesfirePoller* instance, MfDesfirePoller* instance,
const BitBuffer* tx_buffer, const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) { BitBuffer* rx_buffer) {
furi_check(instance); furi_check(instance);
furi_check(instance->iso14443_4a_poller);
furi_check(instance->tx_buffer);
furi_check(instance->rx_buffer);
furi_check(tx_buffer);
furi_check(rx_buffer);
MfDesfireError error = MfDesfireErrorNone; NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK;
Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller(
instance->iso14443_4a_poller,
&status_code,
tx_buffer,
rx_buffer,
instance->command_mode,
instance->tx_buffer,
instance->rx_buffer);
do { if(iso14443_4a_error != Iso14443_4aErrorNone) {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( return mf_desfire_process_error(iso14443_4a_error);
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_desfire_process_error(iso14443_4a_error);
break;
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME);
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
} else {
bit_buffer_reset(rx_buffer);
}
while(
bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_desfire_process_error(iso14443_4a_error);
break;
}
const size_t rx_size = bit_buffer_get_size_bytes(instance->rx_buffer);
const size_t rx_capacity_remaining =
bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer);
if(rx_size <= rx_capacity_remaining + 1) {
bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
} else {
FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1);
}
}
} while(false);
if(error == MfDesfireErrorNone) {
uint8_t err_code = bit_buffer_get_byte(instance->rx_buffer, 0);
error = mf_desfire_process_status_code(err_code);
} }
return error; return mf_desfire_process_status_code(status_code);
} }
MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) { MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {
@@ -102,7 +75,8 @@ MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfi
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
@@ -124,7 +98,8 @@ MfDesfireError
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
@@ -146,7 +121,8 @@ MfDesfireError
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
@@ -170,7 +146,7 @@ MfDesfireError mf_desfire_poller_read_key_version(
bit_buffer_set_byte(instance->input_buffer, 1, key_num); bit_buffer_set_byte(instance->input_buffer, 1, key_num);
MfDesfireError error = MfDesfireError error =
mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error == MfDesfireErrorNone) { if(error == MfDesfireErrorNone) {
if(!mf_desfire_key_version_parse(data, instance->result_buffer)) { if(!mf_desfire_key_version_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol; error = MfDesfireErrorProtocol;
@@ -210,7 +186,8 @@ MfDesfireError
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
@@ -243,7 +220,7 @@ MfDesfireError mf_desfire_poller_select_application(
instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId)); instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));
MfDesfireError error = MfDesfireError error =
mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer);
return error; return error;
} }
@@ -258,7 +235,8 @@ MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, Simple
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
@@ -293,7 +271,8 @@ MfDesfireError mf_desfire_poller_read_file_settings(
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
@@ -329,6 +308,108 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
return error; return error;
} }
MfDesfireError mf_desfire_poller_create_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id,
const MfDesfireKeySettings* key_settings,
uint16_t iso_df_id,
const uint8_t* iso_df_name,
uint8_t iso_df_name_len) {
furi_check(instance);
furi_check(key_settings);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_CREATE_APPLICATION);
bit_buffer_append_bytes(
instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));
mf_desfire_key_settings_dump(key_settings, instance->input_buffer);
if(iso_df_name && iso_df_name_len) {
uint8_t ks2_pos = bit_buffer_get_size_bytes(instance->input_buffer) - 1;
uint8_t ks2 = bit_buffer_get_byte(instance->input_buffer, ks2_pos);
ks2 |= (1 << 5); // Mark file id present
bit_buffer_set_byte(instance->input_buffer, ks2_pos, ks2);
uint8_t iso_df_id_le[sizeof(iso_df_id)];
bit_lib_num_to_bytes_le(iso_df_id, sizeof(iso_df_id_le), iso_df_id_le);
bit_buffer_append_bytes(instance->input_buffer, iso_df_id_le, sizeof(iso_df_id_le));
bit_buffer_append_bytes(instance->input_buffer, iso_df_name, iso_df_name_len);
}
MfDesfireError error =
mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer);
return error;
}
MfDesfireError mf_desfire_poller_create_file(
MfDesfirePoller* instance,
MfDesfireFileId id,
const MfDesfireFileSettings* data,
uint16_t iso_ef_id) {
furi_check(instance);
furi_check(data);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(
instance->input_buffer,
data->type == MfDesfireFileTypeStandard ? MF_DESFIRE_CMD_CREATE_STD_DATA_FILE :
data->type == MfDesfireFileTypeBackup ? MF_DESFIRE_CMD_CREATE_BACKUP_DATA_FILE :
data->type == MfDesfireFileTypeValue ? MF_DESFIRE_CMD_CREATE_VALUE_FILE :
data->type == MfDesfireFileTypeLinearRecord ? MF_DESFIRE_CMD_CREATE_LINEAR_RECORD_FILE :
data->type == MfDesfireFileTypeCyclicRecord ? MF_DESFIRE_CMD_CREATE_CYCLIC_RECORD_FILE :
0x00);
bit_buffer_append_byte(instance->input_buffer, id);
if(iso_ef_id) {
uint8_t iso_ef_id_le[sizeof(iso_ef_id)];
bit_lib_num_to_bytes_le(iso_ef_id, sizeof(iso_ef_id_le), iso_ef_id_le);
bit_buffer_append_bytes(instance->input_buffer, iso_ef_id_le, sizeof(iso_ef_id_le));
}
bit_buffer_append_byte(instance->input_buffer, data->comm);
bit_buffer_append_bytes(
instance->input_buffer,
(const uint8_t*)data->access_rights,
sizeof(MfDesfireFileAccessRights) * data->access_rights_len);
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
uint8_t data_size_le[3 * sizeof(uint8_t)];
bit_lib_num_to_bytes_le(data->data.size, sizeof(data_size_le), data_size_le);
bit_buffer_append_bytes(instance->input_buffer, data_size_le, sizeof(data_size_le));
} else if(data->type == MfDesfireFileTypeValue) {
uint8_t lo_limit_le[sizeof(data->value.lo_limit)];
bit_lib_num_to_bytes_le(data->value.lo_limit, sizeof(lo_limit_le), lo_limit_le);
bit_buffer_append_bytes(instance->input_buffer, lo_limit_le, sizeof(lo_limit_le));
uint8_t hi_limit_le[sizeof(data->value.hi_limit)];
bit_lib_num_to_bytes_le(data->value.hi_limit, sizeof(hi_limit_le), hi_limit_le);
bit_buffer_append_bytes(instance->input_buffer, hi_limit_le, sizeof(hi_limit_le));
uint8_t value_le[sizeof(data->value.limited_credit_value)];
bit_lib_num_to_bytes_le(data->value.limited_credit_value, sizeof(value_le), value_le);
bit_buffer_append_bytes(instance->input_buffer, value_le, sizeof(value_le));
bit_buffer_append_byte(instance->input_buffer, data->value.limited_credit_enabled);
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
uint8_t record_size_le[3 * sizeof(uint8_t)];
bit_lib_num_to_bytes_le(data->record.size, sizeof(record_size_le), record_size_le);
bit_buffer_append_bytes(instance->input_buffer, record_size_le, sizeof(record_size_le));
uint8_t record_max_le[3 * sizeof(uint8_t)];
bit_lib_num_to_bytes_le(data->record.max, sizeof(record_max_le), record_max_le);
bit_buffer_append_bytes(instance->input_buffer, record_max_le, sizeof(record_max_le));
}
MfDesfireError error =
mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer);
return error;
}
static MfDesfireError mf_desfire_poller_read_file( static MfDesfireError mf_desfire_poller_read_file(
MfDesfirePoller* instance, MfDesfirePoller* instance,
MfDesfireFileId id, MfDesfireFileId id,
@@ -354,7 +435,8 @@ static MfDesfireError mf_desfire_poller_read_file(
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&current_offset, 3); bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&current_offset, 3);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3); bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&bytes_to_read, 3);
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;
size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer); size_t bytes_received = bit_buffer_get_size_bytes(instance->result_buffer);
@@ -400,7 +482,8 @@ MfDesfireError mf_desfire_poller_read_file_value(
MfDesfireError error; MfDesfireError error;
do { do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); error = mf_desfire_poller_send_chunks(
instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break; if(error != MfDesfireErrorNone) break;

View File

@@ -30,6 +30,7 @@ typedef enum {
struct MfDesfirePoller { struct MfDesfirePoller {
Iso14443_4aPoller* iso14443_4a_poller; Iso14443_4aPoller* iso14443_4a_poller;
NxpNativeCommandMode command_mode;
MfDesfirePollerSessionState session_state; MfDesfirePollerSessionState session_state;
MfDesfirePollerState state; MfDesfirePollerState state;
MfDesfireError error; MfDesfireError error;

View File

@@ -15,8 +15,8 @@
const uint8_t mf_plus_ats_t1_tk_values[][MF_PLUS_T1_TK_VALUE_LEN] = { const uint8_t mf_plus_ats_t1_tk_values[][MF_PLUS_T1_TK_VALUE_LEN] = {
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X {0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE {0xC1, 0x05, 0x21, 0x30, 0x00, 0xF6, 0xD1}, // Mifare Plus SE
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE {0xC1, 0x05, 0x21, 0x30, 0x10, 0xF6, 0xD1}, // Mifare Plus SE
}; };
MfPlusError mf_plus_get_type_from_version( MfPlusError mf_plus_get_type_from_version(
@@ -27,7 +27,7 @@ MfPlusError mf_plus_get_type_from_version(
MfPlusError error = MfPlusErrorProtocol; MfPlusError error = MfPlusErrorProtocol;
if(mf_plus_data->version.hw_type == 0x02 || mf_plus_data->version.hw_type == 0x82) { if((mf_plus_data->version.hw_type & 0x0F) == 0x02) {
error = MfPlusErrorNone; error = MfPlusErrorNone;
// Mifare Plus EV1/EV2 // Mifare Plus EV1/EV2
@@ -85,16 +85,15 @@ MfPlusError
MfPlusError error = MfPlusErrorProtocol; MfPlusError error = MfPlusErrorProtocol;
if(simple_array_get_count(iso4_data->ats_data.t1_tk) != MF_PLUS_T1_TK_VALUE_LEN) { const size_t historical_bytes_len = simple_array_get_count(iso4_data->ats_data.t1_tk);
if(historical_bytes_len != MF_PLUS_T1_TK_VALUE_LEN) {
return MfPlusErrorProtocol; return MfPlusErrorProtocol;
} }
const uint8_t* historical_bytes = simple_array_cget_data(iso4_data->ats_data.t1_tk);
switch(iso4_data->iso14443_3a_data->sak) { switch(iso4_data->iso14443_3a_data->sak) {
case 0x08: case 0x08:
if(memcmp( if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) {
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[0],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus S 2K SL1 // Mifare Plus S 2K SL1
mf_plus_data->type = MfPlusTypeS; mf_plus_data->type = MfPlusTypeS;
mf_plus_data->size = MfPlusSize2K; mf_plus_data->size = MfPlusSize2K;
@@ -102,11 +101,7 @@ MfPlusError
FURI_LOG_D(TAG, "Mifare Plus S 2K SL1"); FURI_LOG_D(TAG, "Mifare Plus S 2K SL1");
error = MfPlusErrorNone; error = MfPlusErrorNone;
} else if( } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) {
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[1],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus X 2K SL1 // Mifare Plus X 2K SL1
mf_plus_data->type = MfPlusTypeX; mf_plus_data->type = MfPlusTypeX;
mf_plus_data->size = MfPlusSize2K; mf_plus_data->size = MfPlusSize2K;
@@ -115,14 +110,8 @@ MfPlusError
FURI_LOG_D(TAG, "Mifare Plus X 2K SL1"); FURI_LOG_D(TAG, "Mifare Plus X 2K SL1");
error = MfPlusErrorNone; error = MfPlusErrorNone;
} else if( } else if(
memcmp( memcmp(historical_bytes, mf_plus_ats_t1_tk_values[2], historical_bytes_len) == 0 ||
simple_array_get_data(iso4_data->ats_data.t1_tk), memcmp(historical_bytes, mf_plus_ats_t1_tk_values[3], historical_bytes_len) == 0) {
mf_plus_ats_t1_tk_values[2],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 ||
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[3],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus SE 1K SL1 // Mifare Plus SE 1K SL1
mf_plus_data->type = MfPlusTypeSE; mf_plus_data->type = MfPlusTypeSE;
mf_plus_data->size = MfPlusSize1K; mf_plus_data->size = MfPlusSize1K;
@@ -154,10 +143,7 @@ MfPlusError
break; break;
case 0x18: case 0x18:
if(memcmp( if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) {
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[0],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus S 4K SL1 // Mifare Plus S 4K SL1
mf_plus_data->type = MfPlusTypeS; mf_plus_data->type = MfPlusTypeS;
mf_plus_data->size = MfPlusSize4K; mf_plus_data->size = MfPlusSize4K;
@@ -165,11 +151,7 @@ MfPlusError
FURI_LOG_D(TAG, "Mifare Plus S 4K SL1"); FURI_LOG_D(TAG, "Mifare Plus S 4K SL1");
error = MfPlusErrorNone; error = MfPlusErrorNone;
} else if( } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) {
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[1],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus X 4K SL1 // Mifare Plus X 4K SL1
mf_plus_data->type = MfPlusTypeX; mf_plus_data->type = MfPlusTypeX;
mf_plus_data->size = MfPlusSize4K; mf_plus_data->size = MfPlusSize4K;
@@ -183,10 +165,7 @@ MfPlusError
break; break;
case 0x20: case 0x20:
if(memcmp( if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[0], historical_bytes_len) == 0) {
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[0],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus S 2/4K SL3 // Mifare Plus S 2/4K SL3
FURI_LOG_D(TAG, "Mifare Plus S SL3"); FURI_LOG_D(TAG, "Mifare Plus S SL3");
mf_plus_data->type = MfPlusTypeS; mf_plus_data->type = MfPlusTypeS;
@@ -207,21 +186,20 @@ MfPlusError
} else { } else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)"); FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)");
} }
} else if( } else if(memcmp(historical_bytes, mf_plus_ats_t1_tk_values[1], historical_bytes_len) == 0) {
memcmp( // Mifare Plus X 2/4K SL3
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[1],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
mf_plus_data->type = MfPlusTypeX; mf_plus_data->type = MfPlusTypeX;
mf_plus_data->security_level = MfPlusSecurityLevel3; mf_plus_data->security_level = MfPlusSecurityLevel3;
FURI_LOG_D(TAG, "Mifare Plus X SL3"); FURI_LOG_D(TAG, "Mifare Plus X SL3");
if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) { if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {
// Mifare Plus X 2K SL3
mf_plus_data->size = MfPlusSize2K; mf_plus_data->size = MfPlusSize2K;
FURI_LOG_D(TAG, "Mifare Plus X 2K SL3"); FURI_LOG_D(TAG, "Mifare Plus X 2K SL3");
error = MfPlusErrorNone; error = MfPlusErrorNone;
} else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) { } else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {
// Mifare Plus X 4K SL3
mf_plus_data->size = MfPlusSize4K; mf_plus_data->size = MfPlusSize4K;
FURI_LOG_D(TAG, "Mifare Plus X 4K SL3"); FURI_LOG_D(TAG, "Mifare Plus X 4K SL3");
@@ -229,6 +207,16 @@ MfPlusError
} else { } else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)"); FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)");
} }
} else if(
memcmp(historical_bytes, mf_plus_ats_t1_tk_values[2], historical_bytes_len) == 0 ||
memcmp(historical_bytes, mf_plus_ats_t1_tk_values[3], historical_bytes_len) == 0) {
// Mifare Plus SE 1K SL3
mf_plus_data->type = MfPlusTypeSE;
mf_plus_data->size = MfPlusSize1K;
mf_plus_data->security_level = MfPlusSecurityLevel3;
FURI_LOG_D(TAG, "Mifare Plus SE 1K SL3");
error = MfPlusErrorNone;
} else { } else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type"); FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type");
} }
@@ -238,22 +226,12 @@ MfPlusError
} }
MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) {
bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion);
if(can_parse) { if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion)); bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion));
} else if( } else {
bit_buffer_get_size_bytes(buf) == 8 && memset(data, 0, sizeof(MfPlusVersion));
bit_buffer_get_byte(buf, 0) == MF_PLUS_STATUS_ADDITIONAL_FRAME) {
// HACK(-nofl): There are supposed to be three parts to the GetVersion command,
// with the second and third parts fetched by sending the AdditionalFrame
// command. I don't know whether the entire MIFARE Plus line uses status as
// the first byte, so let's just assume we only have the first part of
// the response if it's size 8 and starts with the AF status. The second
// part of the response is the same size and status byte, but so far
// we're only reading one response.
can_parse = true;
bit_buffer_write_bytes_mid(buf, data, 1, bit_buffer_get_size_bytes(buf) - 1);
} }
return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol; return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol;

View File

@@ -2,10 +2,9 @@
#include "mf_plus.h" #include "mf_plus.h"
#define MF_PLUS_FFF_PICC_PREFIX "PICC" #include <nfc/helpers/nxp_native_command.h>
#define MF_PLUS_STATUS_OPERATION_OK (0x90) #define MF_PLUS_FFF_PICC_PREFIX "PICC"
#define MF_PLUS_STATUS_ADDITIONAL_FRAME (0xAF)
MfPlusError mf_plus_get_type_from_version( MfPlusError mf_plus_get_type_from_version(
const Iso14443_4aData* iso14443_4a_data, const Iso14443_4aData* iso14443_4a_data,

View File

@@ -19,28 +19,36 @@ MfPlusError mf_plus_process_error(Iso14443_4aError error) {
} }
} }
MfPlusError mf_plus_poller_send_chunk( MfPlusError mf_plus_process_status_code(uint8_t status_code) {
switch(status_code) {
case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK:
return MfPlusErrorNone;
default:
return MfPlusErrorProtocol;
}
}
MfPlusError mf_plus_poller_send_chunks(
MfPlusPoller* instance, MfPlusPoller* instance,
const BitBuffer* tx_buffer, const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) { BitBuffer* rx_buffer) {
furi_assert(instance); furi_assert(instance);
furi_assert(instance->iso14443_4a_poller);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK;
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller(
MfPlusError error = mf_plus_process_error(iso14443_4a_error); instance->iso14443_4a_poller,
&status_code,
tx_buffer,
rx_buffer,
NxpNativeCommandModePlain,
instance->tx_buffer,
instance->rx_buffer);
if(error == MfPlusErrorNone) { if(iso14443_4a_error != Iso14443_4aErrorNone) {
bit_buffer_copy(rx_buffer, instance->rx_buffer); return mf_plus_process_error(iso14443_4a_error);
} }
bit_buffer_reset(instance->tx_buffer); return mf_plus_process_status_code(status_code);
return error;
} }
MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) { MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) {
@@ -50,7 +58,7 @@ MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* d
bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION); bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION);
MfPlusError error = MfPlusError error =
mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer); mf_plus_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error == MfPlusErrorNone) { if(error == MfPlusErrorNone) {
error = mf_plus_version_parse(data, instance->result_buffer); error = mf_plus_version_parse(data, instance->result_buffer);
} }

View File

@@ -22,9 +22,11 @@
#include <nfc/protocols/mf_classic/mf_classic.h> #include <nfc/protocols/mf_classic/mf_classic.h>
#include <nfc/protocols/mf_plus/mf_plus.h> #include <nfc/protocols/mf_plus/mf_plus.h>
#include <nfc/protocols/mf_desfire/mf_desfire.h> #include <nfc/protocols/mf_desfire/mf_desfire.h>
#include <nfc/protocols/emv/emv.h>
#include <nfc/protocols/slix/slix_device_defs.h> #include <nfc/protocols/slix/slix_device_defs.h>
#include <nfc/protocols/st25tb/st25tb.h> #include <nfc/protocols/st25tb/st25tb.h>
#include <nfc/protocols/ntag4xx/ntag4xx.h>
#include <nfc/protocols/type_4_tag/type_4_tag.h>
#include <nfc/protocols/emv/emv.h>
/** /**
* @brief List of registered NFC device implementations. * @brief List of registered NFC device implementations.
@@ -45,6 +47,8 @@ const NfcDeviceBase* const nfc_devices[NfcProtocolNum] = {
[NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolMfDesfire] = &nfc_device_mf_desfire,
[NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSlix] = &nfc_device_slix,
[NfcProtocolSt25tb] = &nfc_device_st25tb, [NfcProtocolSt25tb] = &nfc_device_st25tb,
[NfcProtocolNtag4xx] = &nfc_device_ntag4xx,
[NfcProtocolType4Tag] = &nfc_device_type_4_tag,
[NfcProtocolEmv] = &nfc_device_emv, [NfcProtocolEmv] = &nfc_device_emv,
/* Add new protocols here */ /* Add new protocols here */
}; };

View File

@@ -3,10 +3,11 @@
#include <nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h> #include <nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h>
#include <nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h> #include <nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h>
#include <nfc/protocols/iso15693_3/iso15693_3_listener_defs.h> #include <nfc/protocols/iso15693_3/iso15693_3_listener_defs.h>
#include <nfc/protocols/felica/felica_listener_defs.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h> #include <nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h>
#include <nfc/protocols/mf_classic/mf_classic_listener_defs.h> #include <nfc/protocols/mf_classic/mf_classic_listener_defs.h>
#include <nfc/protocols/slix/slix_listener_defs.h> #include <nfc/protocols/slix/slix_listener_defs.h>
#include <nfc/protocols/felica/felica_listener_defs.h> #include <nfc/protocols/type_4_tag/type_4_tag_listener_defs.h>
const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum] = { const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum] = {
[NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a,
@@ -14,11 +15,14 @@ const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum] = {
[NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a,
[NfcProtocolIso14443_4b] = NULL, [NfcProtocolIso14443_4b] = NULL,
[NfcProtocolIso15693_3] = &nfc_listener_iso15693_3, [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3,
[NfcProtocolFelica] = &nfc_listener_felica,
[NfcProtocolMfUltralight] = &mf_ultralight_listener, [NfcProtocolMfUltralight] = &mf_ultralight_listener,
[NfcProtocolMfClassic] = &mf_classic_listener, [NfcProtocolMfClassic] = &mf_classic_listener,
[NfcProtocolMfPlus] = NULL,
[NfcProtocolMfDesfire] = NULL, [NfcProtocolMfDesfire] = NULL,
[NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSlix] = &nfc_listener_slix,
[NfcProtocolSt25tb] = NULL, [NfcProtocolSt25tb] = NULL,
[NfcProtocolFelica] = &nfc_listener_felica, [NfcProtocolNtag4xx] = NULL,
[NfcProtocolType4Tag] = &nfc_listener_type_4_tag,
[NfcProtocolEmv] = NULL, [NfcProtocolEmv] = NULL,
}; };

View File

@@ -10,9 +10,11 @@
#include <nfc/protocols/mf_classic/mf_classic_poller_defs.h> #include <nfc/protocols/mf_classic/mf_classic_poller_defs.h>
#include <nfc/protocols/mf_plus/mf_plus_poller_defs.h> #include <nfc/protocols/mf_plus/mf_plus_poller_defs.h>
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h> #include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
#include <nfc/protocols/emv/emv_poller_defs.h>
#include <nfc/protocols/slix/slix_poller_defs.h> #include <nfc/protocols/slix/slix_poller_defs.h>
#include <nfc/protocols/st25tb/st25tb_poller_defs.h> #include <nfc/protocols/st25tb/st25tb_poller_defs.h>
#include <nfc/protocols/ntag4xx/ntag4xx_poller_defs.h>
#include <nfc/protocols/type_4_tag/type_4_tag_poller_defs.h>
#include <nfc/protocols/emv/emv_poller_defs.h>
const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum] = { const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum] = {
[NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a, [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a,
@@ -27,6 +29,8 @@ const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum] = {
[NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller,
[NfcProtocolSlix] = &nfc_poller_slix, [NfcProtocolSlix] = &nfc_poller_slix,
[NfcProtocolSt25tb] = &nfc_poller_st25tb, [NfcProtocolSt25tb] = &nfc_poller_st25tb,
[NfcProtocolNtag4xx] = &ntag4xx_poller,
[NfcProtocolType4Tag] = &type_4_tag_poller,
[NfcProtocolEmv] = &emv_poller, [NfcProtocolEmv] = &emv_poller,
/* Add new pollers here */ /* Add new pollers here */
}; };

View File

@@ -12,19 +12,19 @@
* ``` * ```
* **************************** Protocol tree structure *************************** * **************************** Protocol tree structure ***************************
* *
* (Start) * (Start)
* | * |
* +------------------------+-----------+---------+------------+ * +------------------------+-----------+---------+------------+
* | | | | | * | | | | |
* ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB * ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB
* | | | * | | |
* +---------------+-------------+ ISO14443-4B SLIX * +---------------+-------------+ ISO14443-4B SLIX
* | | | * | | |
* ISO14443-4A Mf Ultralight Mf Classic * ISO14443-4A Mf Ultralight Mf Classic
* | * |
* +-----+-----+ * +-----+----+----------+----------+---------+
* | | * | | | | |
* Mf Desfire EMV * Mf Desfire Mf Plus NTAG4xx Type 4 Tag EMV
* ``` * ```
* *
* When implementing a new protocol, its place in the tree must be determined first. * When implementing a new protocol, its place in the tree must be determined first.
@@ -62,8 +62,10 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = {
/** List of ISO14443-4A child protocols. */ /** List of ISO14443-4A child protocols. */
static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = {
NfcProtocolMfDesfire,
NfcProtocolMfPlus, NfcProtocolMfPlus,
NfcProtocolMfDesfire,
NfcProtocolNtag4xx,
NfcProtocolType4Tag,
NfcProtocolEmv, NfcProtocolEmv,
}; };
@@ -156,6 +158,18 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {
.children_num = 0, .children_num = 0,
.children_protocol = NULL, .children_protocol = NULL,
}, },
[NfcProtocolNtag4xx] =
{
.parent_protocol = NfcProtocolIso14443_4a,
.children_num = 0,
.children_protocol = NULL,
},
[NfcProtocolType4Tag] =
{
.parent_protocol = NfcProtocolIso14443_4a,
.children_num = 0,
.children_protocol = NULL,
},
[NfcProtocolEmv] = [NfcProtocolEmv] =
{ {
.parent_protocol = NfcProtocolIso14443_4a, .parent_protocol = NfcProtocolIso14443_4a,

Some files were not shown because too many files have changed in this diff Show More