From 83ff6fb8bf1c610cc0506f2f00f3ad43df15065b Mon Sep 17 00:00:00 2001 From: Astra Date: Mon, 22 Apr 2024 21:09:06 +0900 Subject: [PATCH] Mifare Plus detection done --- .../protocol_support/mf_plus/mf_plus.c | 7 +- lib/nfc/protocols/mf_plus/mf_plus.c | 105 +++++++-- lib/nfc/protocols/mf_plus/mf_plus.h | 3 + lib/nfc/protocols/mf_plus/mf_plus_i.c | 205 ++++++++++++++++++ lib/nfc/protocols/mf_plus/mf_plus_i.h | 26 ++- lib/nfc/protocols/mf_plus/mf_plus_poller.c | 155 +++++++++++-- lib/nfc/protocols/mf_plus/mf_plus_poller_i.h | 1 - lib/nfc/protocols/nfc_device_defs.c | 2 + targets/f7/api_symbols.csv | 1 + 9 files changed, 464 insertions(+), 41 deletions(-) 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 index 0e3373736..a020c9bbd 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -34,6 +34,10 @@ static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, 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; } @@ -52,7 +56,8 @@ static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); - nfc_render_mf_plus_info(data, NfcProtocolFormatTypeFull, temp_str); + 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)); diff --git a/lib/nfc/protocols/mf_plus/mf_plus.c b/lib/nfc/protocols/mf_plus/mf_plus.c index a21dfe177..d0ee4c9c3 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus.c +++ b/lib/nfc/protocols/mf_plus/mf_plus.c @@ -1,10 +1,35 @@ -#include "mf_plus.h" +#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, @@ -23,12 +48,19 @@ const NfcDeviceBase nfc_device_mf_plus = { MfPlusData* mf_plus_alloc() { 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); } @@ -36,12 +68,22 @@ void mf_plus_free(MfPlusData* 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) { @@ -50,29 +92,51 @@ bool mf_plus_verify(MfPlusData* data, const FuriString* device_type) { } bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) { - // TODO - UNUSED(data); - UNUSED(ff); - UNUSED(version); - return true; + 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) { - // TODO - UNUSED(data); - UNUSED(ff); - return true; + 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_check(data); - furi_check(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); @@ -82,11 +146,20 @@ bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) { 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) { - name = "Mifare Plus"; + 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 { @@ -94,6 +167,8 @@ const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType na } } while(false); + furi_string_free(full_name); + FURI_LOG_D("Mifare Plus", "Name: %s", name); return name; } diff --git a/lib/nfc/protocols/mf_plus/mf_plus.h b/lib/nfc/protocols/mf_plus/mf_plus.h index 6d294472b..11e22cbe1 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus.h +++ b/lib/nfc/protocols/mf_plus/mf_plus.h @@ -83,8 +83,11 @@ typedef struct { MfPlusType type; MfPlusSize size; MfPlusSecurityLevel security_level; + FuriString* device_name; } MfPlusData; +extern const NfcDeviceBase nfc_device_mf_plus; + MfPlusData* mf_plus_alloc(); void mf_plus_free(MfPlusData* data); diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index 8c25f2c0c..c248d2568 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -1,5 +1,13 @@ #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); @@ -8,4 +16,201 @@ bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) { } 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 index e1494b4ab..8ced4bdd0 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.h +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h @@ -2,4 +2,28 @@ #include "mf_plus.h" -bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf); \ No newline at end of file +#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 index fb1e8db39..a218229c5 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.c @@ -9,6 +9,13 @@ #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) { @@ -23,82 +30,194 @@ bool mf_plus_poller_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusD switch(iso4_data->iso14443_3a_data->sak) { case 0x08: - if(memcmp(iso4_data->ats_data.t1_tk, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { + 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(iso4_data->ats_data.t1_tk, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { + } 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(iso4_data->ats_data.t1_tk, "\xC1\x05\x2F\x2F\x00\xF6\xD1", 7) == 0 || - memcmp(iso4_data->ats_data.t1_tk, "\xC1\x05\x2F\x2F\x01\xF6\xD1", 7) == 0) { + 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(iso4_data->ats_data.t1_tk, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { + 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(iso4_data->ats_data.t1_tk, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { + } 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, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { + 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, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { + } 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); @@ -140,8 +259,8 @@ static NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) { } static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) { - instance->error = mf_plus_poller_read_version(instance, &instance->data->version); - if(instance->error == MfPlusErrorNone) { + bool success = mf_plus_poller_detect_type(instance); + if(success) { instance->state = MfPlusPollerStateReadSuccess; } else { instance->state = MfPlusPollerStateReadFailed; @@ -166,7 +285,6 @@ static NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) { iso14443_4a_poller_halt(instance->iso14443_4a_poller); instance->mfp_event.type = MfPlusPollerEventTypeReadSuccess; NfcCommand command = instance->callback(instance->general_event, instance->context); - instance->state = MfPlusPollerStateIdle; return command; } @@ -233,16 +351,7 @@ static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) { bool detected = false; if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { - MfPlusVersion version = {}; - const MfPlusError error = mf_plus_poller_read_version(instance, &version); - if(error == MfPlusErrorNone) { - if(version.hw_major == 0x02 || version.hw_major == 0x82) { - detected = true; - } - } else { - detected = mf_plus_poller_get_type_from_iso4( - iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data); - } + detected = mf_plus_poller_detect_type(instance); } return detected; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h index 3473a8ca3..79f46b8d8 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h @@ -28,7 +28,6 @@ struct MfPlusPoller { Iso14443_4aPoller* iso14443_4a_poller; MfPlusData* data; - MfPlusCardState card_state; MfPlusPollerState state; BitBuffer* tx_buffer; diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 870bcafd9..6a145445c 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 @@ -39,6 +40,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/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 8c888018b..59990c9b2 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3739,6 +3739,7 @@ Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, 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,