diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c new file mode 100644 index 000000000..a020c9bbd --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -0,0 +1,121 @@ +#include "mf_plus.h" +#include "mf_plus_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_mf_plus(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus); + + 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)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_plus_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 NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfPlus); + + NfcApp* instance = context; + const MfPlusPollerEvent* mf_plus_event = event.event_data; + + if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller)); + FURI_LOG_D( + "MFP", + "Read success: %s", + nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_mf_plus(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_plus, instance); +} + +static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_plus_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_mf_plus(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_mf_plus = { + .features = NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_plus, + .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_scene_read_on_enter_mf_plus, + .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_mf_plus, + .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_mf_plus, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h new file mode 100644 index 000000000..7f2e63dd1 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_plus; \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c new file mode 100644 index 000000000..8640fa16d --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.c @@ -0,0 +1,67 @@ +#include "mf_plus_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +void nfc_render_mf_plus_info( + const MfPlusData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(mf_plus_get_base_data(data), str); + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(mf_plus_get_base_data(data), str); +} + +void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) { + nfc_render_mf_plus_version(&data->version, str); +} + +void nfc_render_mf_plus_version(const MfPlusVersion* 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:%02x\n" + "week %d year %d\n", + data->batch[0], + data->batch[1], + data->batch[2], + data->batch[3], + data->batch[4], + data->prod_week, + data->prod_year); +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h new file mode 100644 index 000000000..5aa8436a9 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_plus_info( + const MfPlusData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str); + +void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str); 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 6b42a1660..a80cd6cc0 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 @@ -17,6 +17,7 @@ #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" @@ -39,6 +40,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [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, diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 4fb49f792..d30467740 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -21,6 +21,7 @@ env.Append( File("protocols/iso14443_4b/iso14443_4b.h"), File("protocols/mf_ultralight/mf_ultralight.h"), File("protocols/mf_classic/mf_classic.h"), + File("protocols/mf_plus/mf_plus.h"), File("protocols/mf_desfire/mf_desfire.h"), File("protocols/emv/emv.h"), File("protocols/slix/slix.h"), @@ -33,6 +34,7 @@ env.Append( File("protocols/iso14443_4b/iso14443_4b_poller.h"), File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"), + File("protocols/mf_plus/mf_plus_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/slix/slix_poller.h"), File("protocols/emv/emv_poller.h"), diff --git a/lib/nfc/protocols/mf_plus/mf_plus.c b/lib/nfc/protocols/mf_plus/mf_plus.c new file mode 100644 index 000000000..98b7cd82a --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus.c @@ -0,0 +1,189 @@ +#include "mf_plus_i.h" + +#include +#include + +#define MF_PLUS_PROTOCOL_NAME "Mifare Plus" + +static const char* mf_plus_type_strings[] = { + [MfPlusTypeS] = "Plus S", + [MfPlusTypeX] = "Plus X", + [MfPlusTypeSE] = "Plus SE", + [MfPlusTypeEV1] = "Plus EV1", + [MfPlusTypeEV2] = "Plus EV2", + [MfPlusTypePlus] = "Plus", + [MfPlusTypeUnknown] = "Unknown", +}; + +static const char* mf_plus_size_strings[] = { + [MfPlusSize1K] = "1K", + [MfPlusSize2K] = "2K", + [MfPlusSize4K] = "4K", + [MfPlusSizeUnknown] = "Unknown", +}; + +static const char* mf_plus_security_level_strings[] = { + [MfPlusSecurityLevel0] = "SL0", + [MfPlusSecurityLevel1] = "SL1", + [MfPlusSecurityLevel2] = "SL2", + [MfPlusSecurityLevel3] = "SL3", + [MfPlusSecurityLevelUnknown] = "Unknown", +}; + +const NfcDeviceBase nfc_device_mf_plus = { + .protocol_name = MF_PLUS_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_plus_alloc, + .free = (NfcDeviceFree)mf_plus_free, + .reset = (NfcDeviceReset)mf_plus_reset, + .copy = (NfcDeviceCopy)mf_plus_copy, + .verify = (NfcDeviceVerify)mf_plus_verify, + .load = (NfcDeviceLoad)mf_plus_load, + .save = (NfcDeviceSave)mf_plus_save, + .is_equal = (NfcDeviceEqual)mf_plus_is_equal, + .get_name = (NfcDeviceGetName)mf_plus_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_plus_get_uid, + .set_uid = (NfcDeviceSetUid)mf_plus_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_plus_get_base_data, +}; + +MfPlusData* mf_plus_alloc(void) { + MfPlusData* data = malloc(sizeof(MfPlusData)); + data->device_name = furi_string_alloc(); + data->iso14443_4a_data = iso14443_4a_alloc(); + + data->type = MfPlusTypeUnknown; + data->security_level = MfPlusSecurityLevelUnknown; + data->size = MfPlusSizeUnknown; + + return data; +} + +void mf_plus_free(MfPlusData* data) { + furi_check(data); + furi_string_free(data->device_name); + iso14443_4a_free(data->iso14443_4a_data); + free(data); +} + +void mf_plus_reset(MfPlusData* data) { + furi_check(data); + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->version, 0, sizeof(data->version)); + data->type = MfPlusTypeUnknown; + data->security_level = MfPlusSecurityLevelUnknown; + data->size = MfPlusSizeUnknown; +} + +void mf_plus_copy(MfPlusData* data, const MfPlusData* other) { + furi_check(data); + furi_check(other); + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->version = other->version; + data->type = other->type; + data->security_level = other->security_level; + data->size = other->size; +} + +bool mf_plus_verify(MfPlusData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, MF_PLUS_PROTOCOL_NAME); +} + +bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + if(!mf_plus_version_load(&data->version, ff)) break; + if(!mf_plus_type_load(&data->type, ff)) break; + if(!mf_plus_security_level_load(&data->security_level, ff)) break; + if(!mf_plus_size_load(&data->size, ff)) break; + success = true; + } while(false); + + return success; +} + +bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) { + furi_assert(data); + + bool success = false; + + do { + if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break; + if(!flipper_format_write_comment_cstr(ff, MF_PLUS_PROTOCOL_NAME " specific data")) break; + if(!mf_plus_version_save(&data->version, ff)) break; + if(!mf_plus_type_save(&data->type, ff)) break; + if(!mf_plus_security_level_save(&data->security_level, ff)) break; + if(!mf_plus_size_save(&data->size, ff)) break; + success = true; + } while(false); + + return success; +} + +bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) { + furi_assert(data); + furi_assert(other); + bool equal = false; + + do { + if(!iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data)) break; + if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break; + if(data->security_level != other->security_level) break; + if(data->type != other->type) break; + if(data->size != other->size) break; + equal = true; + } while(false); + + return equal; +} + +const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type) { + furi_check(data); + + FuriString* full_name = furi_string_alloc(); + const char* name = NULL; + + do { + if(name_type == NfcDeviceNameTypeFull) { + furi_string_reset(data->device_name); + furi_string_cat_printf( + data->device_name, + "Mifare %s %s %s", + mf_plus_type_strings[data->type], // Includes "Plus" for regular Mifare Plus cards + mf_plus_size_strings[data->size], + mf_plus_security_level_strings[data->security_level]); + name = furi_string_get_cstr(data->device_name); + FURI_LOG_D("Mifare Plus", "Full name: %s", name); + } else if(name_type == NfcDeviceNameTypeShort) { + name = "Mifare Plus"; + } else { + break; + } + } while(false); + + furi_string_free(full_name); + FURI_LOG_D("Mifare Plus", "Name: %s", name); + return name; +} + +const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} +Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data) { + furi_check(data); + return data->iso14443_4a_data; +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus.h b/lib/nfc/protocols/mf_plus/mf_plus.h new file mode 100644 index 000000000..828d1c070 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_PLUS_UID_SIZE (7) +#define MF_PLUS_BATCH_SIZE (5) + +#define MF_PLUS_CMD_GET_VERSION (0x60) + +typedef enum { + MfPlusErrorNone, + MfPlusErrorUnknown, + MfPlusErrorNotPresent, + MfPlusErrorProtocol, + MfPlusErrorAuth, + MfPlusErrorPartialRead, + MfPlusErrorTimeout, +} MfPlusError; + +typedef enum { + MfPlusTypePlus, + MfPlusTypeEV1, + MfPlusTypeEV2, + MfPlusTypeS, + MfPlusTypeSE, + MfPlusTypeX, + + MfPlusTypeUnknown, + MfPlusTypeNum, +} MfPlusType; + +typedef enum { + MfPlusSize1K, + MfPlusSize2K, + MfPlusSize4K, + + MfPlusSizeUnknown, + MfPlusSizeNum, +} MfPlusSize; + +typedef enum { + MfPlusSecurityLevel0, + MfPlusSecurityLevel1, + MfPlusSecurityLevel2, + MfPlusSecurityLevel3, + + MfPlusSecurityLevelUnknown, + MfPlusSecurityLevelNum, +} MfPlusSecurityLevel; + +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[MF_PLUS_UID_SIZE]; + uint8_t batch[MF_PLUS_BATCH_SIZE]; + uint8_t prod_week; + uint8_t prod_year; +} MfPlusVersion; + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + MfPlusVersion version; + MfPlusType type; + MfPlusSize size; + MfPlusSecurityLevel security_level; + FuriString* device_name; +} MfPlusData; + +extern const NfcDeviceBase nfc_device_mf_plus; + +MfPlusData* mf_plus_alloc(void); + +void mf_plus_free(MfPlusData* data); + +void mf_plus_reset(MfPlusData* data); + +void mf_plus_copy(MfPlusData* data, const MfPlusData* other); + +bool mf_plus_verify(MfPlusData* data, const FuriString* device_type); + +bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version); + +bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff); + +bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other); + +const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len); + +bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c new file mode 100644 index 000000000..c248d2568 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -0,0 +1,216 @@ +#include "mf_plus_i.h" + +#define MF_PLUS_FFF_VERSION_KEY \ + MF_PLUS_FFF_PICC_PREFIX " " \ + "Version" + +#define MF_PLUS_FFF_SECURITY_LEVEL_KEY "Security Level" +#define MF_PLUS_FFF_CARD_TYPE_KEY "Card Type" +#define MF_PLUS_FFF_MEMORY_SIZE_KEY "Memory Size" + +bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion)); + } + + return can_parse; +} + +bool mf_plus_security_level_parse(MfPlusSecurityLevel* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusSecurityLevel); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfPlusSecurityLevel)); + } + + return can_parse; +} + +bool mf_plus_type_parse(MfPlusType* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusType); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfPlusType)); + } + + return can_parse; +} + +bool mf_plus_size_parse(MfPlusSize* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusSize); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfPlusSize)); + } + + return can_parse; +} + +bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, MF_PLUS_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfPlusVersion)); +} + +bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff) { + FuriString* security_level_string = furi_string_alloc(); + flipper_format_read_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string); + + // Take the last character of the string + char security_level_char = furi_string_get_char( + security_level_string, furi_string_utf8_length(security_level_string) - 1); + + switch(security_level_char) { + case '0': + *data = MfPlusSecurityLevel0; + break; + case '1': + *data = MfPlusSecurityLevel1; + break; + case '2': + *data = MfPlusSecurityLevel2; + break; + case '3': + *data = MfPlusSecurityLevel3; + break; + default: + *data = MfPlusSecurityLevelUnknown; + break; + } + + furi_string_free(security_level_string); + + return true; +} + +bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff) { + FuriString* type_string = furi_string_alloc(); + flipper_format_read_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string); + + if(furi_string_equal_str(type_string, "Mifare Plus")) { + *data = MfPlusTypePlus; + } else if(furi_string_equal_str(type_string, "Mifare Plus X")) { + *data = MfPlusTypeX; + } else if(furi_string_equal_str(type_string, "Mifare Plus S")) { + *data = MfPlusTypeS; + } else if(furi_string_equal_str(type_string, "Mifare Plus SE")) { + *data = MfPlusTypeSE; + } else if(furi_string_equal_str(type_string, "Mifare Plus EV1")) { + *data = MfPlusTypeEV1; + } else if(furi_string_equal_str(type_string, "Mifare Plus EV2")) { + *data = MfPlusTypeEV2; + } else { + *data = MfPlusTypeUnknown; + } + + furi_string_free(type_string); + return true; +} + +bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff) { + FuriString* size_string = furi_string_alloc(); + flipper_format_read_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string); + + if(furi_string_equal_str(size_string, "1K")) { + *data = MfPlusSize1K; + } else if(furi_string_equal_str(size_string, "2K")) { + *data = MfPlusSize2K; + } else if(furi_string_equal_str(size_string, "4K")) { + *data = MfPlusSize4K; + } else { + *data = MfPlusSizeUnknown; + } + + furi_string_free(size_string); + return true; +} + +bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, MF_PLUS_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfPlusVersion)); +} + +bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff) { + FuriString* security_level_string = furi_string_alloc(); + + switch(*data) { + case MfPlusSecurityLevel0: + furi_string_cat(security_level_string, "SL0"); + break; + case MfPlusSecurityLevel1: + furi_string_cat(security_level_string, "SL1"); + break; + case MfPlusSecurityLevel2: + furi_string_cat(security_level_string, "SL2"); + break; + case MfPlusSecurityLevel3: + furi_string_cat(security_level_string, "SL3"); + break; + default: + furi_string_cat(security_level_string, "Unknown"); + break; + } + + flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string); + furi_string_free(security_level_string); + + return true; +} + +bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) { + FuriString* type_string = furi_string_alloc(); + + switch(*data) { + case MfPlusTypePlus: + furi_string_cat(type_string, "Mifare Plus"); + break; + case MfPlusTypeX: + furi_string_cat(type_string, "Mifare Plus X"); + break; + case MfPlusTypeS: + furi_string_cat(type_string, "Mifare Plus S"); + break; + case MfPlusTypeSE: + furi_string_cat(type_string, "Mifare Plus SE"); + break; + case MfPlusTypeEV1: + furi_string_cat(type_string, "Mifare Plus EV1"); + break; + case MfPlusTypeEV2: + furi_string_cat(type_string, "Mifare Plus EV2"); + break; + default: + furi_string_cat(type_string, "Unknown"); + break; + } + + flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string); + furi_string_free(type_string); + + return true; +} + +bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) { + FuriString* size_string = furi_string_alloc(); + + switch(*data) { + case MfPlusSize1K: + furi_string_cat(size_string, "1K"); + break; + case MfPlusSize2K: + furi_string_cat(size_string, "2K"); + break; + case MfPlusSize4K: + furi_string_cat(size_string, "4K"); + break; + default: + furi_string_cat(size_string, "Unknown"); + break; + } + + flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string); + furi_string_free(size_string); + + return true; +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.h b/lib/nfc/protocols/mf_plus/mf_plus_i.h new file mode 100644 index 000000000..8ced4bdd0 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h @@ -0,0 +1,29 @@ +#pragma once + +#include "mf_plus.h" + +#define MF_PLUS_FFF_PICC_PREFIX "PICC" + +bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf); + +bool mf_plus_security_level_parse(MfPlusSecurityLevel* data, const BitBuffer* buf); + +bool mf_plus_type_parse(MfPlusType* data, const BitBuffer* buf); + +bool mf_plus_size_parse(MfPlusSize* data, const BitBuffer* buf); + +bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff); + +bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff); + +bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff); + +bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff); + +bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff); + +bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff); + +bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff); + +bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff); diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.c b/lib/nfc/protocols/mf_plus/mf_plus_poller.c new file mode 100644 index 000000000..a218229c5 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.c @@ -0,0 +1,367 @@ +#include "mf_plus_poller_i.h" + +#include + +#include + +#define TAG "MfPlusPoller" + +#define MF_PLUS_BUF_SIZE (64U) +#define MF_PLUS_RESULT_BUF_SIZE (512U) + +const char* mf_plus_ats_t1_tk_values[] = { + "\xC1\x05\x2F\x2F\x00\x35\xC7", // Mifare Plus S + "\xC1\x05\x2F\x2F\x01\xBC\xD6", // Mifare Plus X + "\xC1\x05\x2F\x2F\x00\xF6\xD1", // Mifare Plus SE + "\xC1\x05\x2F\x2F\x01\xF6\xD1", // Mifare Plus SE +}; + +typedef NfcCommand (*MfPlusPollerReadHandler)(MfPlusPoller* instance); + +const MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +bool mf_plus_poller_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) { + furi_assert(iso4_data); + furi_assert(mf_plus_data); + + switch(iso4_data->iso14443_3a_data->sak) { + case 0x08: + if(memcmp( + 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 + mf_plus_data->type = MfPlusTypeS; + mf_plus_data->size = MfPlusSize2K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + FURI_LOG_D(TAG, "Mifare Plus S 2K SL1"); + return true; + } else if( + 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 + mf_plus_data->type = MfPlusTypeX; + mf_plus_data->size = MfPlusSize2K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + FURI_LOG_D(TAG, "Mifare Plus X 2K SL1"); + return true; + } else if( + memcmp( + simple_array_get_data(iso4_data->ats_data.t1_tk), + 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 + mf_plus_data->type = MfPlusTypeSE; + mf_plus_data->size = MfPlusSize1K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + FURI_LOG_D(TAG, "Mifare Plus SE 1K SL1"); + return true; + } else { + FURI_LOG_D(TAG, "Sak 08 but no known Mifare Plus type"); + return false; + } + case 0x18: + if(memcmp( + 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 + mf_plus_data->type = MfPlusTypeS; + mf_plus_data->size = MfPlusSize4K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + FURI_LOG_D(TAG, "Mifare Plus S 4K SL1"); + return true; + } else if( + 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 + mf_plus_data->type = MfPlusTypeX; + mf_plus_data->size = MfPlusSize4K; + mf_plus_data->security_level = MfPlusSecurityLevel1; + FURI_LOG_D(TAG, "Mifare Plus X 4K SL1"); + return true; + } else { + FURI_LOG_D(TAG, "Sak 18 but no known Mifare Plus type"); + return false; + } + case 0x20: + if(memcmp( + 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 + mf_plus_data->type = MfPlusTypeS; + mf_plus_data->security_level = MfPlusSecurityLevel3; + + if(iso4_data->iso14443_3a_data->atqa[1] == 0x04) { + // Mifare Plus S 2K SL3 + mf_plus_data->size = MfPlusSize2K; + FURI_LOG_D(TAG, "Mifare Plus S 2K SL3"); + } else if(iso4_data->iso14443_3a_data->atqa[1] == 0x02) { + // Mifare Plus S 4K SL3 + mf_plus_data->size = MfPlusSize4K; + FURI_LOG_D(TAG, "Mifare Plus S 4K SL3"); + } else { + FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)"); + return false; + } + return true; + + } else if( + memcmp( + 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->security_level = MfPlusSecurityLevel3; + + if(iso4_data->iso14443_3a_data->atqa[1] == 0x04) { + mf_plus_data->size = MfPlusSize2K; + FURI_LOG_D(TAG, "Mifare Plus X 2K SL3"); + } else if(iso4_data->iso14443_3a_data->atqa[1] == 0x02) { + mf_plus_data->size = MfPlusSize4K; + FURI_LOG_D(TAG, "Mifare Plus X 4K SL3"); + } else { + FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)"); + return false; + } + return true; + } else { + FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type"); + return false; + } + } + + FURI_LOG_D(TAG, "No known Mifare Plus type"); + return false; +} + +static bool mf_plus_poller_detect_type(MfPlusPoller* instance) { + furi_assert(instance); + + bool detected = false; + + const Iso14443_4aData* iso14443_4a_data = + iso14443_4a_poller_get_data(instance->iso14443_4a_poller); + const MfPlusError error = mf_plus_poller_read_version(instance, &instance->data->version); + if(error == MfPlusErrorNone) { + FURI_LOG_D(TAG, "Read version success: %d", error); + if(instance->data->version.hw_major == 0x02 || instance->data->version.hw_major == 0x82) { + detected = true; + if(iso14443_4a_data->iso14443_3a_data->sak == 0x10) { + // Mifare Plus 2K SL2 + instance->data->type = MfPlusTypePlus; + instance->data->size = MfPlusSize2K; + instance->data->security_level = MfPlusSecurityLevel2; + } else if(iso14443_4a_data->iso14443_3a_data->sak == 0x11) { + // Mifare Plus 4K SL3 + instance->data->type = MfPlusTypePlus; + instance->data->size = MfPlusSize4K; + instance->data->security_level = MfPlusSecurityLevel3; + } else { + // Mifare Plus EV1/EV2 + + // Revision + switch(instance->data->version.hw_major) { + case 0x11: + instance->data->type = MfPlusTypeEV1; + break; + case 0x22: + instance->data->type = MfPlusTypeEV2; + break; + default: + instance->data->type = MfPlusTypeUnknown; + break; + } + + // Storage size + switch(instance->data->version.hw_storage) { + case 0x16: + instance->data->size = MfPlusSize2K; + break; + case 0x18: + instance->data->size = MfPlusSize4K; + break; + default: + instance->data->size = MfPlusSizeUnknown; + break; + } + + // Security level + if(iso14443_4a_data->iso14443_3a_data->sak == 0x20) { + // Mifare Plus EV1/2 SL3 + instance->data->security_level = MfPlusSecurityLevel3; + } else { + // Mifare Plus EV1/2 SL1 + instance->data->security_level = MfPlusSecurityLevel1; + } + } + } + + } else { + FURI_LOG_D(TAG, "Read version error: %d", error); + detected = mf_plus_poller_get_type_from_iso4(iso14443_4a_data, instance->data); + } + + return detected; +} + +MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + furi_assert(iso14443_4a_poller); + + MfPlusPoller* instance = malloc(sizeof(MfPlusPoller)); + furi_assert(instance); + + instance->iso14443_4a_poller = iso14443_4a_poller; + + instance->data = mf_plus_alloc(); + + instance->tx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(MF_PLUS_RESULT_BUF_SIZE); + + instance->general_event.protocol = NfcProtocolMfPlus; + instance->general_event.event_data = &instance->mfp_event; + instance->general_event.instance = instance; + + instance->mfp_event.data = &instance->mfp_event_data; + + return instance; +} + +static NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) { + furi_assert(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 = MfPlusPollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) { + bool success = mf_plus_poller_detect_type(instance); + if(success) { + instance->state = MfPlusPollerStateReadSuccess; + } else { + instance->state = MfPlusPollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_plus_poller_handler_read_failed(MfPlusPoller* instance) { + furi_assert(instance); + FURI_LOG_D(TAG, "Read failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->mfp_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = MfPlusPollerStateIdle; + return command; +} + +static NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) { + furi_assert(instance); + FURI_LOG_D(TAG, "Read success"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->mfp_event.type = MfPlusPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const MfPlusPollerReadHandler mf_plus_poller_read_handler[MfPlusPollerStateNum] = { + [MfPlusPollerStateIdle] = mf_plus_poller_handler_idle, + [MfPlusPollerStateReadVersion] = mf_plus_poller_handler_read_version, + [MfPlusPollerStateReadFailed] = mf_plus_poller_handler_read_failed, + [MfPlusPollerStateReadSuccess] = mf_plus_poller_handler_read_success, +}; + +static void mf_plus_poller_set_callback( + MfPlusPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol = NfcProtocolIso14443_4a); + + MfPlusPoller* instance = context; + furi_assert(instance); + + 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 = mf_plus_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->mfp_event.type = MfPlusPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +void mf_plus_poller_free(MfPlusPoller* instance) { + furi_assert(instance); + furi_assert(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); + mf_plus_free(instance->data); + free(instance); +} + +static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol = NfcProtocolIso14443_4a); + + MfPlusPoller* instance = context; + furi_assert(instance); + + Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + bool detected = false; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + detected = mf_plus_poller_detect_type(instance); + } + + return detected; +} + +const NfcPollerBase mf_plus_poller = { + .alloc = (NfcPollerAlloc)mf_plus_poller_alloc, + .free = (NfcPollerFree)mf_plus_poller_free, + .set_callback = (NfcPollerSetCallback)mf_plus_poller_set_callback, + .run = (NfcPollerRun)mf_plus_poller_run, + .detect = (NfcPollerDetect)mf_plus_poller_detect, + .get_data = (NfcPollerGetData)mf_plus_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.h b/lib/nfc/protocols/mf_plus/mf_plus_poller.h new file mode 100644 index 000000000..7e892366f --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.h @@ -0,0 +1,55 @@ +#pragma once + +#include "mf_plus.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MIFARE Plus poller opaque type definition. + */ +typedef struct MfPlusPoller MfPlusPoller; + +/** + * @brief Enumeration of possible MfPlus poller event types. + */ + +typedef enum { + MfPlusPollerEventTypeReadSuccess, /**< Card was read successfully. */ + MfPlusPollerEventTypeReadFailed, /**< Poller failed to read the card. */ +} MfPlusPollerEventType; + +/** + * @brief MIFARE Plus poller event data. + */ +typedef union { + MfPlusError error; /**< Error code indicating card reading fail reason. */ +} MfPlusPollerEventData; + +/** + * @brief MIFARE Plus poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + MfPlusPollerEventType type; /**< Type of emitted event. */ + MfPlusPollerEventData* data; /**< Pointer to event specific data. */ +} MfPlusPollerEvent; + +/** + * @brief Read MfPlus card version. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfPlusVersion structure to be filled with version data. + * @return MfPlusErrorNone on success, an error code on failure. + */ +MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h b/lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h new file mode 100644 index 000000000..366eb5116 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase mf_plus_poller; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c new file mode 100644 index 000000000..1a4298b67 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c @@ -0,0 +1,73 @@ +#include "mf_plus_poller_i.h" + +#include + +#include "mf_plus_i.h" + +#define TAG "MfPlusPoller" + +MfPlusError mf_plus_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return MfPlusErrorNone; + case Iso14443_4aErrorNotPresent: + return MfPlusErrorNotPresent; + case Iso14443_4aErrorTimeout: + return MfPlusErrorTimeout; + default: + return MfPlusErrorProtocol; + } +} + +MfPlusError + mf_plus_send_chunk(MfPlusPoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { + 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); + + MfPlusError error = MfPlusErrorNone; + + do { + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + error = mf_plus_process_error(iso14443_4a_error); + break; + } + + bit_buffer_reset(instance->tx_buffer); + + 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(false); + + return error; +} + +MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION); + + MfPlusError error; + + do { + error = mf_plus_send_chunk(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfPlusErrorNone) break; + + if(!mf_plus_version_parse(data, instance->result_buffer)) { + error = MfPlusErrorProtocol; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h new file mode 100644 index 000000000..79f46b8d8 --- /dev/null +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h @@ -0,0 +1,54 @@ +#pragma once + +#include "mf_plus_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_PLUS_FWT_FC (60000) + +typedef enum { + MfPlusCardStateDetected, + MfPlusCardStateLost, +} MfPlusCardState; + +typedef enum { + MfPlusPollerStateIdle, + MfPlusPollerStateReadVersion, + MfPlusPollerStateReadFailed, + MfPlusPollerStateReadSuccess, + + MfPlusPollerStateNum, +} MfPlusPollerState; + +struct MfPlusPoller { + Iso14443_4aPoller* iso14443_4a_poller; + + MfPlusData* data; + MfPlusPollerState state; + + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + + MfPlusError error; + NfcGenericEvent general_event; + MfPlusPollerEvent mfp_event; + MfPlusPollerEventData mfp_event_data; + NfcGenericCallback callback; + void* context; +}; + +MfPlusError mf_plus_process_error(Iso14443_4aError error); + +MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller); + +void mf_plus_poller_free(MfPlusPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index e09523f23..36a58a243 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolFelica] = &nfc_device_felica, [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, [NfcProtocolMfClassic] = &nfc_device_mf_classic, + [NfcProtocolMfPlus] = &nfc_device_mf_plus, [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, [NfcProtocolSlix] = &nfc_device_slix, [NfcProtocolSt25tb] = &nfc_device_st25tb, diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index e79c96d98..a4717f1b1 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolFelica] = &nfc_poller_felica, [NfcProtocolMfUltralight] = &mf_ultralight_poller, [NfcProtocolMfClassic] = &mf_classic_poller, + [NfcProtocolMfPlus] = &mf_plus_poller, [NfcProtocolMfDesfire] = &mf_desfire_poller, [NfcProtocolSlix] = &nfc_poller_slix, [NfcProtocolSt25tb] = &nfc_poller_st25tb, diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 17c39fc9f..4106e689a 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -63,6 +63,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { /** List of ISO14443-4A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfDesfire, + NfcProtocolMfPlus, NfcProtocolEmv, }; @@ -131,6 +132,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + [NfcProtocolMfPlus] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, [NfcProtocolMfDesfire] = { .parent_protocol = NfcProtocolIso14443_4a, diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index 39e8045fe..12866528e 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -184,6 +184,7 @@ typedef enum { NfcProtocolFelica, NfcProtocolMfUltralight, NfcProtocolMfClassic, + NfcProtocolMfPlus, NfcProtocolMfDesfire, NfcProtocolSlix, NfcProtocolSt25tb, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 7a790b0e1..e0d592d9c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -156,6 +156,8 @@ Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,, Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h,, Header,+,lib/nfc/protocols/mf_desfire/mf_desfire.h,, Header,+,lib/nfc/protocols/mf_desfire/mf_desfire_poller.h,, +Header,+,lib/nfc/protocols/mf_plus/mf_plus.h,, +Header,+,lib/nfc/protocols/mf_plus/mf_plus_poller.h,, 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,, @@ -2563,6 +2565,19 @@ Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" Function,+,mf_desfire_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*" +Function,+,mf_plus_alloc,MfPlusData*, +Function,+,mf_plus_copy,void,"MfPlusData*, const MfPlusData*" +Function,+,mf_plus_free,void,MfPlusData* +Function,+,mf_plus_get_base_data,Iso14443_4aData*,const MfPlusData* +Function,+,mf_plus_get_device_name,const char*,"const MfPlusData*, NfcDeviceNameType" +Function,+,mf_plus_get_uid,const uint8_t*,"const MfPlusData*, size_t*" +Function,+,mf_plus_is_equal,_Bool,"const MfPlusData*, const MfPlusData*" +Function,+,mf_plus_load,_Bool,"MfPlusData*, FlipperFormat*, uint32_t" +Function,+,mf_plus_poller_read_version,MfPlusError,"MfPlusPoller*, MfPlusVersion*" +Function,+,mf_plus_reset,void,MfPlusData* +Function,+,mf_plus_save,_Bool,"const MfPlusData*, FlipperFormat*" +Function,+,mf_plus_set_uid,_Bool,"MfPlusData*, const uint8_t*, size_t" +Function,+,mf_plus_verify,_Bool,"MfPlusData*, const FuriString*" Function,+,mf_ultralight_alloc,MfUltralightData*, Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*" Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData* @@ -3905,6 +3920,7 @@ Variable,-,nfc_device_emv,const NfcDeviceBase, Variable,-,nfc_device_felica,const NfcDeviceBase, 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_st25tb,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence,