diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index dd724b13a..ac019bc7d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -21,6 +21,7 @@ #include "mf_desfire/mf_desfire.h" #include "slix/slix.h" #include "st25tb/st25tb.h" +#include "ntag4xx/ntag4xx.h" #include "type_4_tag/type_4_tag.h" /** @@ -44,6 +45,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, + [NfcProtocolNtag4xx] = &nfc_protocol_support_ntag4xx, [NfcProtocolType4Tag] = &nfc_protocol_support_type_4_tag, /* Add new protocol support implementations here */ }; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c new file mode 100644 index 000000000..851b4ad97 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c @@ -0,0 +1,138 @@ +#include "ntag4xx.h" +#include "ntag4xx_render.h" + +#include + +#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, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h new file mode 100644 index 000000000..09a8388fa --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_ntag4xx; diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c new file mode 100644 index 000000000..0cb587726 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.c @@ -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); +} diff --git a/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h new file mode 100644 index 000000000..ca81cf4c4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#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); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index f0ca22aa7..ad9057d03 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -27,6 +27,7 @@ env.Append( File("protocols/mf_desfire/mf_desfire.h"), File("protocols/slix/slix.h"), File("protocols/st25tb/st25tb.h"), + File("protocols/ntag4xx/ntag4xx.h"), File("protocols/type_4_tag/type_4_tag.h"), # Pollers File("protocols/iso14443_3a/iso14443_3a_poller.h"), @@ -41,6 +42,7 @@ env.Append( File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), + File("protocols/ntag4xx/ntag4xx_poller.h"), File("protocols/type_4_tag/type_4_tag_poller.h"), # Listeners File("protocols/iso14443_3a/iso14443_3a_listener.h"), diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index df7ebde40..1963faeb0 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /** @@ -45,6 +46,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, + [NfcProtocolNtag4xx] = &nfc_device_ntag4xx, [NfcProtocolType4Tag] = &nfc_device_type_4_tag, /* Add new protocols here */ }; diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index a4f9e225a..b0c24023f 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -22,5 +22,6 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = NULL, [NfcProtocolSlix] = &nfc_listener_slix, [NfcProtocolSt25tb] = NULL, + [NfcProtocolNtag4xx] = NULL, [NfcProtocolType4Tag] = &nfc_listener_type_4_tag, }; diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index d11b74eb6..cbbe145db 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -12,6 +12,7 @@ #include #include #include +#include #include const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { @@ -27,6 +28,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, [NfcProtocolSt25tb] = &nfc_poller_st25tb, + [NfcProtocolNtag4xx] = &ntag4xx_poller, [NfcProtocolType4Tag] = &type_4_tag_poller, /* Add new pollers here */ }; diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 7fd7ffb6d..34fa843b5 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -22,9 +22,9 @@ * | | | * ISO14443-4A Mf Ultralight Mf Classic * | - * +-----+------+----------+ - * | | | - * Mf Desfire Type 4 Tag Mf Plus + * +-----+------+----------+----------+ + * | | | | + * Mf Desfire Type 4 Tag Mf Plus NTAG4xx * ``` * * When implementing a new protocol, its place in the tree must be determined first. @@ -64,6 +64,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfPlus, NfcProtocolMfDesfire, + NfcProtocolNtag4xx, NfcProtocolType4Tag, }; @@ -156,6 +157,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolNtag4xx] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, [NfcProtocolType4Tag] = { .parent_protocol = NfcProtocolIso14443_4a, diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index db03c74e7..03caa3979 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -188,6 +188,7 @@ typedef enum { NfcProtocolMfDesfire, NfcProtocolSlix, NfcProtocolSt25tb, + NfcProtocolNtag4xx, NfcProtocolType4Tag, /* Add new protocols here */ diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.c b/lib/nfc/protocols/ntag4xx/ntag4xx.c new file mode 100644 index 000000000..7b0991903 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.c @@ -0,0 +1,192 @@ +#include "ntag4xx_i.h" + +#include + +#define NTAG4XX_PROTOCOL_NAME "NTAG4xx" + +#define NTAG4XX_HW_MAJOR_TYPE_413_DNA (0x10) +#define NTAG4XX_HW_MAJOR_TYPE_424_DNA (0x30) + +#define NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG (0x08) + +static const char* ntag4xx_type_strings[] = { + [Ntag4xxType413DNA] = "NTAG413 DNA", + [Ntag4xxType424DNA] = "NTAG424 DNA", + [Ntag4xxType424DNATT] = "NTAG424 DNA TagTamper", + [Ntag4xxType426QDNA] = "NTAG426Q DNA", + [Ntag4xxType426QDNATT] = "NTAG426Q DNA TagTamper", + [Ntag4xxTypeUnknown] = "UNK", +}; + +const NfcDeviceBase nfc_device_ntag4xx = { + .protocol_name = NTAG4XX_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)ntag4xx_alloc, + .free = (NfcDeviceFree)ntag4xx_free, + .reset = (NfcDeviceReset)ntag4xx_reset, + .copy = (NfcDeviceCopy)ntag4xx_copy, + .verify = (NfcDeviceVerify)ntag4xx_verify, + .load = (NfcDeviceLoad)ntag4xx_load, + .save = (NfcDeviceSave)ntag4xx_save, + .is_equal = (NfcDeviceEqual)ntag4xx_is_equal, + .get_name = (NfcDeviceGetName)ntag4xx_get_device_name, + .get_uid = (NfcDeviceGetUid)ntag4xx_get_uid, + .set_uid = (NfcDeviceSetUid)ntag4xx_set_uid, + .get_base_data = (NfcDeviceGetBaseData)ntag4xx_get_base_data, +}; + +Ntag4xxData* ntag4xx_alloc(void) { + Ntag4xxData* data = malloc(sizeof(Ntag4xxData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + data->device_name = furi_string_alloc(); + return data; +} + +void ntag4xx_free(Ntag4xxData* data) { + furi_check(data); + + ntag4xx_reset(data); + iso14443_4a_free(data->iso14443_4a_data); + furi_string_free(data->device_name); + free(data); +} + +void ntag4xx_reset(Ntag4xxData* data) { + furi_check(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->version, 0, sizeof(Ntag4xxVersion)); +} + +void ntag4xx_copy(Ntag4xxData* data, const Ntag4xxData* other) { + furi_check(data); + furi_check(other); + + ntag4xx_reset(data); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->version = other->version; +} + +bool ntag4xx_verify(Ntag4xxData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool ntag4xx_load(Ntag4xxData* data, FlipperFormat* ff, uint32_t version) { + furi_check(data); + furi_check(ff); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + if(!ntag4xx_version_load(&data->version, ff)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool ntag4xx_save(const Ntag4xxData* data, FlipperFormat* ff) { + furi_check(data); + furi_check(ff); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, NTAG4XX_PROTOCOL_NAME " specific data")) break; + if(!ntag4xx_version_save(&data->version, ff)) break; + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool ntag4xx_is_equal(const Ntag4xxData* data, const Ntag4xxData* other) { + furi_check(data); + furi_check(other); + + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + memcmp(&data->version, &other->version, sizeof(Ntag4xxVersion)) == 0; +} + +Ntag4xxType ntag4xx_get_type_from_version(const Ntag4xxVersion* const version) { + Ntag4xxType type = Ntag4xxTypeUnknown; + + switch(version->hw_major) { + case NTAG4XX_HW_MAJOR_TYPE_413_DNA: + type = Ntag4xxType413DNA; + break; + case NTAG4XX_HW_MAJOR_TYPE_424_DNA: + if(version->hw_subtype & NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG) { + type = Ntag4xxType424DNATT; + } else { + type = Ntag4xxType424DNA; + } + break; + // TODO: there is no info online or in other implementations (NXP TagInfo, NFC Tools, Proxmark3) + // about what the HWMajorVersion is supposed to be for NTAG426Q DNA, and they don't seem to be for sale + // case NTAG4XX_HW_MAJOR_TYPE_426Q_DNA: + // if(version->hw_subtype & NTAG4XX_HW_SUBTYPE_TAGTAMPER_FLAG) { + // type = Ntag4xxType426QDNATT; + // } else { + // type = Ntag4xxType426QDNA; + // } + // break; + default: + break; + } + + return type; +} + +const char* ntag4xx_get_device_name(const Ntag4xxData* data, NfcDeviceNameType name_type) { + furi_check(data); + + const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version); + + if(type == Ntag4xxTypeUnknown) { + furi_string_printf(data->device_name, "Unknown %s", NTAG4XX_PROTOCOL_NAME); + } else { + furi_string_printf(data->device_name, "%s", ntag4xx_type_strings[type]); + if(name_type == NfcDeviceNameTypeShort) { + furi_string_replace(data->device_name, "TagTamper", "TT"); + } + } + + return furi_string_get_cstr(data->device_name); +} + +const uint8_t* ntag4xx_get_uid(const Ntag4xxData* data, size_t* uid_len) { + furi_check(data); + furi_check(uid_len); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool ntag4xx_set_uid(Ntag4xxData* data, const uint8_t* uid, size_t uid_len) { + furi_check(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* ntag4xx_get_base_data(const Ntag4xxData* data) { + furi_check(data); + + return data->iso14443_4a_data; +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx.h b/lib/nfc/protocols/ntag4xx/ntag4xx.h new file mode 100644 index 000000000..85dd1e52d --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NTAG4XX_UID_SIZE (7) +#define NTAG4XX_BATCH_SIZE (4) +#define NTAG4XX_BATCH_EXTRA_BITS 4 +#define NTAG4XX_FAB_KEY_SIZE_BITS_4 4 +#define NTAG4XX_FAB_KEY_SIZE_BITS_1 1 +#define NTAG4XX_PROD_WEEK_SIZE_BITS 7 + +#define NTAG4XX_CMD_GET_VERSION (0x60) + +typedef enum { + Ntag4xxErrorNone, + Ntag4xxErrorNotPresent, + Ntag4xxErrorProtocol, + Ntag4xxErrorTimeout, + Ntag4xxErrorAuthentication, + Ntag4xxErrorCommandNotSupported, +} Ntag4xxError; + +typedef enum { + Ntag4xxType413DNA, + Ntag4xxType424DNA, + Ntag4xxType424DNATT, + Ntag4xxType426QDNA, + Ntag4xxType426QDNATT, + + Ntag4xxTypeUnknown, + Ntag4xxTypeNum, +} Ntag4xxType; + +#pragma pack(push, 1) +typedef struct { + uint8_t hw_vendor; + uint8_t hw_type; + uint8_t hw_subtype; + uint8_t hw_major; + uint8_t hw_minor; + uint8_t hw_storage; + uint8_t hw_proto; + + uint8_t sw_vendor; + uint8_t sw_type; + uint8_t sw_subtype; + uint8_t sw_major; + uint8_t sw_minor; + uint8_t sw_storage; + uint8_t sw_proto; + + uint8_t uid[NTAG4XX_UID_SIZE]; + // [36b batch][5b fab key][7b prod week] + // 5b fab key is split 4b in last byte of batch and 1b in prod week + // Due to endianness, they appear swapped in the struct definition + uint8_t batch[NTAG4XX_BATCH_SIZE]; + struct { + uint8_t fab_key_4b : NTAG4XX_FAB_KEY_SIZE_BITS_4; + uint8_t batch_extra : NTAG4XX_BATCH_EXTRA_BITS; + }; + struct { + uint8_t prod_week : NTAG4XX_PROD_WEEK_SIZE_BITS; + uint8_t fab_key_1b : NTAG4XX_FAB_KEY_SIZE_BITS_1; + }; + uint8_t prod_year; + struct { + uint8_t fab_key_id; + } optional; +} Ntag4xxVersion; +#pragma pack(pop) + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + Ntag4xxVersion version; + FuriString* device_name; +} Ntag4xxData; + +extern const NfcDeviceBase nfc_device_ntag4xx; + +// Virtual methods + +Ntag4xxData* ntag4xx_alloc(void); + +void ntag4xx_free(Ntag4xxData* data); + +void ntag4xx_reset(Ntag4xxData* data); + +void ntag4xx_copy(Ntag4xxData* data, const Ntag4xxData* other); + +bool ntag4xx_verify(Ntag4xxData* data, const FuriString* device_type); + +bool ntag4xx_load(Ntag4xxData* data, FlipperFormat* ff, uint32_t version); + +bool ntag4xx_save(const Ntag4xxData* data, FlipperFormat* ff); + +bool ntag4xx_is_equal(const Ntag4xxData* data, const Ntag4xxData* other); + +const char* ntag4xx_get_device_name(const Ntag4xxData* data, NfcDeviceNameType name_type); + +const uint8_t* ntag4xx_get_uid(const Ntag4xxData* data, size_t* uid_len); + +bool ntag4xx_set_uid(Ntag4xxData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* ntag4xx_get_base_data(const Ntag4xxData* data); + +// Helpers + +Ntag4xxType ntag4xx_get_type_from_version(const Ntag4xxVersion* const version); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c new file mode 100644 index 000000000..6a0c7114a --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.c @@ -0,0 +1,58 @@ +#include "ntag4xx_i.h" + +#define TAG "Ntag4xx" + +#define NTAG4XX_FFF_VERSION_KEY \ + NTAG4XX_FFF_PICC_PREFIX " " \ + "Version" + +Ntag4xxError ntag4xx_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return Ntag4xxErrorNone; + case Iso14443_4aErrorNotPresent: + return Ntag4xxErrorNotPresent; + case Iso14443_4aErrorTimeout: + return Ntag4xxErrorTimeout; + default: + return Ntag4xxErrorProtocol; + } +} + +Ntag4xxError ntag4xx_process_status_code(uint8_t status_code) { + switch(status_code) { + case NTAG4XX_STATUS_OPERATION_OK: + return Ntag4xxErrorNone; + case NTAG4XX_STATUS_AUTHENTICATION_ERROR: + return Ntag4xxErrorAuthentication; + case NTAG4XX_STATUS_ILLEGAL_COMMAND_CODE: + return Ntag4xxErrorCommandNotSupported; + default: + return Ntag4xxErrorProtocol; + } +} + +bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf) { + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const bool can_parse = buf_size == sizeof(Ntag4xxVersion) || + buf_size == sizeof(Ntag4xxVersion) - sizeof(data->optional); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(Ntag4xxVersion)); + if(buf_size < sizeof(Ntag4xxVersion)) { + memset(&data->optional, 0, sizeof(data->optional)); + } + } + + return can_parse && data->hw_type == 0x04; +} + +bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, NTAG4XX_FFF_VERSION_KEY, (uint8_t*)data, sizeof(Ntag4xxVersion)); +} + +bool ntag4xx_version_save(const Ntag4xxVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, NTAG4XX_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(Ntag4xxVersion)); +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h new file mode 100644 index 000000000..db6dabd4b --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_i.h @@ -0,0 +1,59 @@ +#pragma once + +#include "ntag4xx.h" + +#define NTAG4XX_FFF_PICC_PREFIX "PICC" + +// ISO 7816 command wrapping +#define NTAG4XX_CMD_ISO_CLA (0x90) +#define NTAG4XX_CMD_ISO_P1 (0x00) +#define NTAG4XX_CMD_ISO_P2 (0x00) +#define NTAG4XX_CMD_ISO_LE (0x00) +// ISO 7816 status wrapping +#define NTAG4XX_STATUS_ISO_SW1 (0x91) + +// Successful operation +#define NTAG4XX_STATUS_OPERATION_OK (0x00) +// Command code not supported +#define NTAG4XX_STATUS_ILLEGAL_COMMAND_CODE (0x1C) +// CRC or MAC does not match data Padding bytes not valid +#define NTAG4XX_STATUS_INTEGRITY_ERROR (0x1E) +// Invalid key number specified +#define NTAG4XX_STATUS_NO_SUCH_KEY (0x40) +// Length of command string invalid +#define NTAG4XX_STATUS_LENGTH_ERROR (0x7E) +// Current configuration / status does not allow the requested command +#define NTAG4XX_STATUS_PERMISSION_DENIED (0x9D) +// Value of the parameter(s) invalid +#define NTAG4XX_STATUS_PARAMETER_ERROR (0x9E) +// Currently not allowed to authenticate. Keep trying until full delay is spent +#define NTAG4XX_STATUS_AUTHENTICATION_DELAY (0xAD) +// Current authentication status does not allow the requested command +#define NTAG4XX_STATUS_AUTHENTICATION_ERROR (0xAE) +// Additional data frame is expected to be sent +#define NTAG4XX_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 NTAG4XX_STATUS_BOUNDARY_ERROR (0xBE) +// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD +#define NTAG4XX_STATUS_COMMAND_ABORTED (0xCA) +// Specified file number does not exist +#define NTAG4XX_STATUS_FILE_NOT_FOUND (0xF0) + +// Internal helpers + +Ntag4xxError ntag4xx_process_error(Iso14443_4aError error); + +Ntag4xxError ntag4xx_process_status_code(uint8_t status_code); + +// Parse internal Ntag4xx structures + +bool ntag4xx_version_parse(Ntag4xxVersion* data, const BitBuffer* buf); + +// Load internal Ntag4xx structures + +bool ntag4xx_version_load(Ntag4xxVersion* data, FlipperFormat* ff); + +// Save internal Ntag4xx structures + +bool ntag4xx_version_save(const Ntag4xxVersion* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c new file mode 100644 index 000000000..38e6f19d4 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.c @@ -0,0 +1,165 @@ +#include "ntag4xx_poller_i.h" + +#include + +#include + +#define TAG "Ntag4xxPoller" + +#define NTAG4XX_BUF_SIZE (64U) +#define NTAG4XX_RESULT_BUF_SIZE (512U) + +typedef NfcCommand (*Ntag4xxPollerReadHandler)(Ntag4xxPoller* instance); + +static const Ntag4xxData* ntag4xx_poller_get_data(Ntag4xxPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Ntag4xxPoller* ntag4xx_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + Ntag4xxPoller* instance = malloc(sizeof(Ntag4xxPoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = ntag4xx_alloc(); + instance->tx_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(NTAG4XX_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(NTAG4XX_RESULT_BUF_SIZE); + + instance->ntag4xx_event.data = &instance->ntag4xx_event_data; + + instance->general_event.protocol = NfcProtocolNtag4xx; + instance->general_event.event_data = &instance->ntag4xx_event; + instance->general_event.instance = instance; + + return instance; +} + +static void ntag4xx_poller_free(Ntag4xxPoller* instance) { + furi_assert(instance); + + ntag4xx_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + bit_buffer_free(instance->input_buffer); + bit_buffer_free(instance->result_buffer); + free(instance); +} + +static NfcCommand ntag4xx_poller_handler_idle(Ntag4xxPoller* instance) { + bit_buffer_reset(instance->input_buffer); + bit_buffer_reset(instance->result_buffer); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + iso14443_4a_copy( + instance->data->iso14443_4a_data, + iso14443_4a_poller_get_data(instance->iso14443_4a_poller)); + + instance->state = Ntag4xxPollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand ntag4xx_poller_handler_read_version(Ntag4xxPoller* instance) { + instance->error = ntag4xx_poller_read_version(instance, &instance->data->version); + if(instance->error == Ntag4xxErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->state = Ntag4xxPollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = Ntag4xxPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand ntag4xx_poller_handler_read_failed(Ntag4xxPoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadFailed; + instance->ntag4xx_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = Ntag4xxPollerStateIdle; + return command; +} + +static NfcCommand ntag4xx_poller_handler_read_success(Ntag4xxPoller* instance) { + FURI_LOG_D(TAG, "Read success"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Ntag4xxPollerReadHandler ntag4xx_poller_read_handler[Ntag4xxPollerStateNum] = { + [Ntag4xxPollerStateIdle] = ntag4xx_poller_handler_idle, + [Ntag4xxPollerStateReadVersion] = ntag4xx_poller_handler_read_version, + [Ntag4xxPollerStateReadFailed] = ntag4xx_poller_handler_read_failed, + [Ntag4xxPollerStateReadSuccess] = ntag4xx_poller_handler_read_success, +}; + +static void ntag4xx_poller_set_callback( + Ntag4xxPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand ntag4xx_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Ntag4xxPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + command = ntag4xx_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->ntag4xx_event.type = Ntag4xxPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool ntag4xx_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + Ntag4xxPoller* instance = context; + furi_assert(instance); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + bool protocol_detected = false; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + do { + Ntag4xxError error = ntag4xx_poller_read_version(instance, &instance->data->version); + if(error != Ntag4xxErrorNone) break; + + protocol_detected = true; + } while(false); + } + + return protocol_detected; +} + +const NfcPollerBase ntag4xx_poller = { + .alloc = (NfcPollerAlloc)ntag4xx_poller_alloc, + .free = (NfcPollerFree)ntag4xx_poller_free, + .set_callback = (NfcPollerSetCallback)ntag4xx_poller_set_callback, + .run = (NfcPollerRun)ntag4xx_poller_run, + .detect = (NfcPollerDetect)ntag4xx_poller_detect, + .get_data = (NfcPollerGetData)ntag4xx_poller_get_data, +}; diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h new file mode 100644 index 000000000..ce7adc785 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller.h @@ -0,0 +1,43 @@ +#pragma once + +#include "ntag4xx.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Ntag4xxPoller opaque type definition. + */ +typedef struct Ntag4xxPoller Ntag4xxPoller; + +/** + * @brief Enumeration of possible Ntag4xx poller event types. + */ +typedef enum { + Ntag4xxPollerEventTypeReadSuccess, /**< Card was read successfully. */ + Ntag4xxPollerEventTypeReadFailed, /**< Poller failed to read card. */ +} Ntag4xxPollerEventType; + +/** + * @brief Ntag4xx poller event data. + */ +typedef union { + Ntag4xxError error; /**< Error code indicating card reading fail reason. */ +} Ntag4xxPollerEventData; + +/** + * @brief Ntag4xx poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Ntag4xxPollerEventType type; /**< Type of emmitted event. */ + Ntag4xxPollerEventData* data; /**< Pointer to event specific data. */ +} Ntag4xxPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h new file mode 100644 index 000000000..ac0cdce9b --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase ntag4xx_poller; diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c new file mode 100644 index 000000000..9fc53e0a5 --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.c @@ -0,0 +1,113 @@ +#include "ntag4xx_poller_i.h" + +#include + +#include "ntag4xx_i.h" + +#define TAG "Ntag4xxPoller" + +Ntag4xxError ntag4xx_poller_send_chunks( + Ntag4xxPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + 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); + + Ntag4xxError error = Ntag4xxErrorNone; + uint8_t status_code = NTAG4XX_STATUS_OPERATION_OK; + + do { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_CLA); + bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_byte(tx_buffer, 0)); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P1); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P2); + if(bit_buffer_get_size_bytes(tx_buffer) > 1) { + bit_buffer_append_byte(instance->tx_buffer, bit_buffer_get_size_bytes(tx_buffer) - 1); + bit_buffer_append_right(instance->tx_buffer, tx_buffer, 1); + } + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_LE); + + 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 = ntag4xx_process_error(iso14443_4a_error); + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_CLA); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_STATUS_ADDITIONAL_FRAME); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P1); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_P2); + bit_buffer_append_byte(instance->tx_buffer, NTAG4XX_CMD_ISO_LE); + + size_t response_len = bit_buffer_get_size_bytes(instance->rx_buffer); + status_code = NTAG4XX_STATUS_LENGTH_ERROR; + bit_buffer_reset(rx_buffer); + if(response_len >= 2 * sizeof(uint8_t) && + bit_buffer_get_byte(instance->rx_buffer, response_len - 2) == NTAG4XX_STATUS_ISO_SW1) { + status_code = bit_buffer_get_byte(instance->rx_buffer, response_len - 1); + if(response_len > 2 * sizeof(uint8_t)) { + bit_buffer_copy_left( + rx_buffer, instance->rx_buffer, response_len - 2 * sizeof(uint8_t)); + } + } + + while(status_code == NTAG4XX_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 = ntag4xx_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); + + status_code = rx_size < 2 ? NTAG4XX_STATUS_LENGTH_ERROR : + bit_buffer_get_byte(instance->rx_buffer, rx_size - 1); + if(rx_size <= rx_capacity_remaining + 2) { + bit_buffer_set_size_bytes(instance->rx_buffer, rx_size - 2); + bit_buffer_append(rx_buffer, instance->rx_buffer); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); + } + } + } while(false); + + if(error == Ntag4xxErrorNone) { + error = ntag4xx_process_status_code(status_code); + } + + return error; +} + +Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data) { + furi_check(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, NTAG4XX_CMD_GET_VERSION); + + Ntag4xxError error; + + do { + error = + ntag4xx_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != Ntag4xxErrorNone) break; + + if(!ntag4xx_version_parse(data, instance->result_buffer)) { + error = Ntag4xxErrorProtocol; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h new file mode 100644 index 000000000..b77e694ef --- /dev/null +++ b/lib/nfc/protocols/ntag4xx/ntag4xx_poller_i.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ntag4xx_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Ntag4xxPollerStateIdle, + Ntag4xxPollerStateReadVersion, + Ntag4xxPollerStateReadFailed, + Ntag4xxPollerStateReadSuccess, + + Ntag4xxPollerStateNum, +} Ntag4xxPollerState; + +struct Ntag4xxPoller { + Iso14443_4aPoller* iso14443_4a_poller; + Ntag4xxPollerState state; + Ntag4xxError error; + Ntag4xxData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + Ntag4xxPollerEventData ntag4xx_event_data; + Ntag4xxPollerEvent ntag4xx_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +Ntag4xxError ntag4xx_poller_read_version(Ntag4xxPoller* instance, Ntag4xxVersion* data); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 71d065f75..385b687dc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -167,6 +167,8 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, +Header,+,lib/nfc/protocols/ntag4xx/ntag4xx.h,, +Header,+,lib/nfc/protocols/ntag4xx/ntag4xx_poller.h,, Header,+,lib/nfc/protocols/slix/slix.h,, Header,+,lib/nfc/protocols/slix/slix_listener.h,, Header,+,lib/nfc/protocols/slix/slix_poller.h,, @@ -2905,6 +2907,19 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] +Function,+,ntag4xx_alloc,Ntag4xxData*, +Function,+,ntag4xx_copy,void,"Ntag4xxData*, const Ntag4xxData*" +Function,+,ntag4xx_free,void,Ntag4xxData* +Function,+,ntag4xx_get_base_data,Iso14443_4aData*,const Ntag4xxData* +Function,+,ntag4xx_get_device_name,const char*,"const Ntag4xxData*, NfcDeviceNameType" +Function,+,ntag4xx_get_type_from_version,Ntag4xxType,const Ntag4xxVersion* +Function,+,ntag4xx_get_uid,const uint8_t*,"const Ntag4xxData*, size_t*" +Function,+,ntag4xx_is_equal,_Bool,"const Ntag4xxData*, const Ntag4xxData*" +Function,+,ntag4xx_load,_Bool,"Ntag4xxData*, FlipperFormat*, uint32_t" +Function,+,ntag4xx_reset,void,Ntag4xxData* +Function,+,ntag4xx_save,_Bool,"const Ntag4xxData*, FlipperFormat*" +Function,+,ntag4xx_set_uid,_Bool,"Ntag4xxData*, const uint8_t*, size_t" +Function,+,ntag4xx_verify,_Bool,"Ntag4xxData*, const FuriString*" Function,+,number_input_alloc,NumberInput*, Function,+,number_input_free,void,NumberInput* Function,+,number_input_get_view,View*,NumberInput* @@ -4045,6 +4060,7 @@ Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_plus,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, +Variable,-,nfc_device_ntag4xx,const NfcDeviceBase, Variable,-,nfc_device_st25tb,const NfcDeviceBase, Variable,-,nfc_device_type_4_tag,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence,