Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into mntm-dev --nobuild

This commit is contained in:
Willy-JL
2024-05-04 03:35:56 +01:00
34 changed files with 614 additions and 1427 deletions

View File

@@ -17,14 +17,21 @@ void ibutton_scene_delete_confirm_on_enter(void* context) {
ibutton_protocols_render_uid(ibutton->protocols, key, uid);
furi_string_cat_printf(
uid,
"\n%s %s",
ibutton_protocols_get_manufacturer(ibutton->protocols, ibutton_key_get_protocol_id(key)),
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
furi_string_cat(tmp, uid);
furi_string_push_back(tmp, '\n');
const char* protocol =
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key));
const char* manufacturer =
ibutton_protocols_get_manufacturer(ibutton->protocols, ibutton_key_get_protocol_id(key));
if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, "N/A") != 0) {
furi_string_cat_printf(tmp, "%s ", manufacturer);
}
furi_string_cat(tmp, protocol);
widget_add_text_box_element(
widget, 0, 0, 128, 64, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);

View File

@@ -21,17 +21,14 @@ void ibutton_scene_emulate_on_enter(void* context) {
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
if(furi_string_empty(ibutton->file_path)) {
furi_string_printf(
tmp,
"Unsaved\n%s",
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
} else {
furi_string_printf(tmp, "%s", ibutton->key_name);
}
furi_string_printf(
tmp,
"[%s]\n%s",
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)),
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name);
widget_add_text_box_element(
widget, 52, 23, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
widget, 52, 24, 75, 40, AlignCenter, AlignTop, furi_string_get_cstr(tmp), true);
widget_add_string_multiline_element(
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "Emulating");

View File

@@ -10,12 +10,23 @@ void ibutton_scene_info_on_enter(void* context) {
FuriString* tmp = furi_string_alloc();
FuriString* brief_data = furi_string_alloc();
furi_string_printf(
tmp,
"Name:%s\n\e#%s %s\e#\n",
ibutton->key_name,
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
if((strcmp(
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
ibutton_protocols_get_name(ibutton->protocols, protocol_id)) != 0) &&
(strcmp(ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), "N/A") != 0)) {
furi_string_printf(
tmp,
"Name:%s\n\e#%s %s\e#\n",
ibutton->key_name,
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
} else {
furi_string_printf(
tmp,
"Name:%s\n\e#%s\e#\n",
ibutton->key_name,
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
}
ibutton_protocols_render_brief_data(ibutton->protocols, key, brief_data);

View File

@@ -40,15 +40,14 @@ void ibutton_scene_write_on_enter(void* context) {
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
if(furi_string_empty(ibutton->file_path)) {
furi_string_printf(
tmp, "Unsaved\n%s", ibutton_protocols_get_name(ibutton->protocols, protocol_id));
} else {
furi_string_printf(tmp, "%s", ibutton->key_name);
}
furi_string_printf(
tmp,
"[%s]\n%s",
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name);
widget_add_text_box_element(
widget, 52, 23, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
widget, 52, 24, 75, 40, AlignCenter, AlignTop, furi_string_get_cstr(tmp), true);
ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);

View File

@@ -15,16 +15,15 @@ void lfrfid_scene_emulate_on_enter(void* context) {
FuriString* display_text = furi_string_alloc_set("\e#Emulating\e#\n");
if(furi_string_empty(app->file_name)) {
furi_string_cat(display_text, "Unsaved\n");
furi_string_cat(display_text, protocol_dict_get_name(app->dict, app->protocol_id));
} else {
furi_string_cat(display_text, app->file_name);
}
furi_string_cat_printf(
display_text,
"[%s]\n%s",
protocol_dict_get_name(app->dict, app->protocol_id),
furi_string_empty(app->file_name) ? "Unsaved Tag" : furi_string_get_cstr(app->file_name));
widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64);
widget_add_text_box_element(
widget, 55, 16, 67, 48, AlignCenter, AlignTop, furi_string_get_cstr(display_text), true);
widget, 51, 6, 79, 50, AlignCenter, AlignTop, furi_string_get_cstr(display_text), false);
furi_string_free(display_text);

View File

@@ -25,12 +25,18 @@ void lfrfid_scene_write_on_enter(void* context) {
popup_set_header(popup, "Writing", 94, 16, AlignCenter, AlignTop);
if(!furi_string_empty(app->file_name)) {
popup_set_text(popup, furi_string_get_cstr(app->file_name), 94, 29, AlignCenter, AlignTop);
snprintf(
app->text_store,
LFRFID_TEXT_STORE_SIZE,
"[%s]\n%s",
protocol_dict_get_name(app->dict, app->protocol_id),
furi_string_get_cstr(app->file_name));
popup_set_text(popup, app->text_store, 94, 29, AlignCenter, AlignTop);
} else {
snprintf(
app->text_store,
LFRFID_TEXT_STORE_SIZE,
"Unsaved\n%s",
"[%s]\nUnsaved Tag",
protocol_dict_get_name(app->dict, app->protocol_id));
popup_set_text(popup, app->text_store, 94, 29, AlignCenter, AlignTop);
}

View File

@@ -22,8 +22,14 @@ void lfrfid_scene_write_and_set_pass_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_header(popup, "Writing\nwith password", 89, 30, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
popup_set_header(popup, "Writing\nwith\npassword", 94, 8, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
snprintf(
app->text_store,
LFRFID_TEXT_STORE_SIZE,
"[%s]",
protocol_dict_get_name(app->dict, app->protocol_id));
popup_set_text(popup, app->text_store, 94, 45, AlignCenter, AlignTop);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);

View File

@@ -1,121 +0,0 @@
#include "mf_plus.h"
#include "mf_plus_render.h"
#include <nfc/protocols/mf_plus/mf_plus_poller.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
#include "../iso14443_4a/iso14443_4a_i.h"
static void nfc_scene_info_on_enter_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,
},
};

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
#pragma once
#include <nfc/protocols/mf_plus/mf_plus.h>
#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);

View File

@@ -17,7 +17,6 @@
#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"
@@ -40,7 +39,6 @@ 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,

View File

@@ -15,12 +15,10 @@ struct HidMouseJiggler {
typedef struct {
bool connected;
bool running;
int interval_idx;
uint8_t counter;
int min_interval; // Minimum interval for random range
int max_interval; // Maximum interval for random range
} HidMouseJigglerModel;
const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000};
static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseJigglerModel* model = context;
@@ -34,22 +32,26 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
}
#endif
// Title "Mouse Jiggler"
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler");
// Timeout
elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):");
canvas_set_font(canvas, FontSecondary);
if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7);
if(model->interval_idx != (int)COUNT_OF(intervals) - 1)
canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
furi_string_free(interval_str);
// Display the current min interval in minutes
canvas_set_font(canvas, FontSecondary); // Assuming there's a smaller font available
FuriString* min_interval_str = furi_string_alloc_printf("Min: %d min", model->min_interval);
elements_multiline_text_aligned(
canvas, 0, 16, AlignLeft, AlignTop, furi_string_get_cstr(min_interval_str));
furi_string_free(min_interval_str);
// Display the current max interval in minutes
FuriString* max_interval_str = furi_string_alloc_printf("Max: %d min", model->max_interval);
elements_multiline_text_aligned(
canvas, 0, 28, AlignLeft, AlignTop, furi_string_get_cstr(max_interval_str));
furi_string_free(max_interval_str);
// "Press Start to jiggle"
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, AlignLeft, 50, "Press Start\nto jiggle");
// Ok
canvas_draw_icon(canvas, 63, 30, &I_Space_65x18);
@@ -78,11 +80,20 @@ static void hid_mouse_jiggler_timer_callback(void* context) {
HidMouseJigglerModel * model,
{
if(model->running) {
model->counter++;
hid_hal_mouse_move(
hid_mouse_jiggler->hid,
(model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT,
0);
// Generate a random interval in minutes and convert to milliseconds
int randomIntervalMinutes =
model->min_interval + rand() % (model->max_interval - model->min_interval + 1);
// Randomize the mouse movement distance and direction
int move_x = (rand() % 2001) - 1000; // Randomly between -1000 and 1000
int move_y = (rand() % 2001) - 1000; // Randomly between -1000 and 1000
// Perform the mouse move with the randomized values
hid_hal_mouse_move(hid_mouse_jiggler->hid, move_x, move_y);
// Restart timer with the new random interval
furi_timer_stop(hid_mouse_jiggler->timer);
furi_timer_start(hid_mouse_jiggler->timer, randomIntervalMinutes * 60000);
}
},
false);
@@ -104,23 +115,51 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{
if(event->type == InputTypePress && event->key == InputKeyOk) {
model->running = !model->running;
if(model->running) {
furi_timer_stop(hid_mouse_jiggler->timer);
furi_timer_start(hid_mouse_jiggler->timer, intervals[model->interval_idx]);
};
consumed = true;
}
if(event->type == InputTypePress && event->key == InputKeyRight && !model->running &&
model->interval_idx < (int)COUNT_OF(intervals) - 1) {
model->interval_idx++;
consumed = true;
}
if(event->type == InputTypePress && event->key == InputKeyLeft && !model->running &&
model->interval_idx > 0) {
model->interval_idx--;
consumed = true;
if(event->type == InputTypePress) {
switch(event->key) {
case InputKeyOk:
model->running = !model->running;
if(model->running) {
furi_timer_stop(hid_mouse_jiggler->timer);
int randomIntervalMinutes =
model->min_interval +
rand() % (model->max_interval - model->min_interval + 1);
furi_timer_start(hid_mouse_jiggler->timer, randomIntervalMinutes * 60000);
}
consumed = true;
break;
case InputKeyUp:
if(!model->running && model->min_interval < model->max_interval) {
model->min_interval++; // Increment min interval by 1 minute
}
consumed = true;
break;
case InputKeyDown:
if(!model->running && model->min_interval > 1) { // Minimum 1 minute
model->min_interval--; // Decrement min interval by 1 minute
}
consumed = true;
break;
case InputKeyRight:
if(!model->running && model->max_interval < 30) { // Maximum 30 minutes
model->max_interval++; // Increment max interval by 1 minute
}
consumed = true;
break;
case InputKeyLeft:
if(!model->running && model->max_interval > model->min_interval + 1) {
model->max_interval--; // Decrement max interval by 1 minute
}
consumed = true;
break;
default:
break;
}
}
},
true);
@@ -145,7 +184,14 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler);
with_view_model(
hid_mouse_jiggler->view, HidMouseJigglerModel * model, { model->interval_idx = 2; }, true);
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{
// Initialize the min and max interval values
model->min_interval = 2; // 2 minutes
model->max_interval = 15; // 15 minutes
},
true);
return hid_mouse_jiggler;
}

View File

@@ -2,9 +2,6 @@
#include <gui/view.h>
#define MOUSE_MOVE_SHORT 5
#define MOUSE_MOVE_LONG 20
typedef struct Hid Hid;
typedef struct HidMouseJiggler HidMouseJiggler;

View File

@@ -1,5 +1,6 @@
#include "lfrfid_protocols.h"
#include "protocol_em4100.h"
#include "protocol_electra.h"
#include "protocol_h10301.h"
#include "protocol_idteck.h"
#include "protocol_indala26.h"
@@ -22,6 +23,7 @@ const ProtocolBase* lfrfid_protocols[] = {
[LFRFIDProtocolEM4100] = &protocol_em4100,
[LFRFIDProtocolEM4100_32] = &protocol_em4100_32,
[LFRFIDProtocolEM4100_16] = &protocol_em4100_16,
[LFRFIDProtocolElectra] = &protocol_electra,
[LFRFIDProtocolH10301] = &protocol_h10301,
[LFRFIDProtocolIdteck] = &protocol_idteck,
[LFRFIDProtocolIndala26] = &protocol_indala26,

View File

@@ -11,6 +11,7 @@ typedef enum {
LFRFIDProtocolEM4100,
LFRFIDProtocolEM4100_32,
LFRFIDProtocolEM4100_16,
LFRFIDProtocolElectra,
LFRFIDProtocolH10301,
LFRFIDProtocolIdteck,
LFRFIDProtocolIndala26,

View File

@@ -0,0 +1,439 @@
/*
* Electra intercom rfid protocol (Romania)
*
* Based on EM4100 protocol implementation from https://github.com/flipperdevices/flipperzero-firmware/blob/dev/lib/lfrfid/protocols/protocol_em4100.c
*
* Copyright 2024 Leptoptilos <leptoptilos@icloud.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ------------------------------------------------------------------------------------------------------------------------------
* PROTOCOL DESCRIPTION:
* ------------------------------------------------------------------------------------------------------------------------------
* Electra intercom 125 kHz protocol based on 64-bit clock EM4100, but includes some extra data after base EM4100 data (epilogue)
*
* Epilogue size is 64 bits, but only first 16 bits matter. Rest 6 bytes - some filler data,
* that arbitrary change is not validated by the Electra intercoms
*
* There are curently three known types of epilogue:
* - 0x7E71AAAAAAAAAAAA (AA filler)
* - 0x7E71000000000000 (00 filler)
* - 0x0030AAAAAAAAAAAA
*
* First two epilogue bytes may be interpreted as EM4100 data continuation
* Nevertheless, these bytes have correct row parity bits, but have not correct collumn parity
* For example: 0x7E71AAAAAAAAAAAA epilogue:
*
* In binary: | 0b01111110 | 01110001 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 |
* In hex: | 0x7E | 71 | AA | AA | AA | AA | AA | AA |
*
* As EM4100 data:
* 0111 1 // 7
* 1100 0 // C
* 0111 1 // 7
* 1101 0 // here epilogue filler starts (from second bit)
* 1010 1 // there is no correct raw parity bits anymore
* 0101 0
* 1010 1
* 0101 0
* 1010 // and no correct column parity
*/
#include "bit_lib/bit_lib.h"
#include <furi.h>
#include <stdlib.h>
#include <toolbox/protocols/protocol.h>
#include <toolbox/manchester_decoder.h>
#include "lfrfid_protocols.h"
#define TAG "ELECTRA"
typedef uint64_t ElectraDecodedData;
#define EM_HEADER_POS (55)
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
#define EM_FIRST_ROW_POS (50)
#define EM_ROW_COUNT (10)
#define EM_COLUMN_COUNT (4)
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
#define EM_COLUMN_POS (4)
#define ELECTRA_STOP_POS (0)
#define ELECTRA_STOP_MASK (0x1LLU << ELECTRA_STOP_POS)
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | ELECTRA_STOP_MASK)
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
#define ELECTRA_DECODED_BASE_DATA_SIZE (5)
#define ELECTRA_ENCODED_BASE_DATA_SIZE (sizeof(ElectraDecodedData))
#define ELECTRA_DECODED_EPILOGUE_SIZE (3)
#define ELECTRA_ENCODED_EPILOGUE_SIZE (sizeof(ElectraDecodedData))
#define ELECTRA_DECODED_DATA_SIZE (ELECTRA_DECODED_BASE_DATA_SIZE + ELECTRA_DECODED_EPILOGUE_SIZE)
#define ELECTRA_ENCODED_DATA_SIZE (ELECTRA_ENCODED_BASE_DATA_SIZE + ELECTRA_ENCODED_EPILOGUE_SIZE)
#define ELECTRA_DECODED_DATA_EPILOGUE_START_POS (ELECTRA_DECODED_BASE_DATA_SIZE)
#define ELECTRA_CLOCK_PER_BIT (64)
#define ELECTRA_READ_SHORT_TIME (256)
#define ELECTRA_READ_LONG_TIME (512)
#define ELECTRA_READ_JITTER_TIME (100)
#define ELECTRA_READ_SHORT_TIME_LOW (ELECTRA_READ_SHORT_TIME - ELECTRA_READ_JITTER_TIME)
#define ELECTRA_READ_SHORT_TIME_HIGH (ELECTRA_READ_SHORT_TIME + ELECTRA_READ_JITTER_TIME)
#define ELECTRA_READ_LONG_TIME_LOW (ELECTRA_READ_LONG_TIME - ELECTRA_READ_JITTER_TIME)
#define ELECTRA_READ_LONG_TIME_HIGH (ELECTRA_READ_LONG_TIME + ELECTRA_READ_JITTER_TIME)
#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)
typedef struct {
uint8_t data[ELECTRA_DECODED_DATA_SIZE];
ElectraDecodedData encoded_base_data;
ElectraDecodedData encoded_epilogue;
uint8_t encoded_data_index;
bool encoded_polarity;
ManchesterState decoder_manchester_state;
} ProtocolElectra;
ProtocolElectra* protocol_electra_alloc(void) {
ProtocolElectra* proto = malloc(sizeof(ProtocolElectra));
return (void*)proto;
};
void protocol_electra_free(ProtocolElectra* proto) {
free(proto);
};
uint8_t* protocol_electra_get_data(ProtocolElectra* proto) {
return proto->data;
};
static void electra_decode(
const uint8_t* encoded_base_data,
const uint8_t encoded_base_data_size,
const uint8_t* encoded_epilogue,
const uint8_t encoded_epilogue_size,
uint8_t* decoded_data,
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= ELECTRA_DECODED_DATA_SIZE);
furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE);
furi_check(encoded_epilogue_size >= ELECTRA_ENCODED_EPILOGUE_SIZE);
uint8_t decoded_data_index = 0;
ElectraDecodedData base_data = *((ElectraDecodedData*)(encoded_base_data));
//ElectraDecodedData epilogue = *((ElectraDecodedData*)(encoded_epilogue));
// clean result
memset(decoded_data, 0, decoded_data_size);
// header
for(uint8_t i = 0; i < 9; i++) {
base_data = base_data << 1;
}
// nibbles
uint8_t value = 0;
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
uint8_t nibble = 0;
for(uint8_t i = 0; i < 5; i++) {
if(i < 4) nibble = (nibble << 1) | (base_data & (1LLU << 63) ? 1 : 0);
base_data = base_data << 1;
}
value = (value << 4) | nibble;
if(r % 2) {
decoded_data[decoded_data_index] |= value;
decoded_data_index++;
value = 0;
}
}
// copy first 3 bytes of encoded epilogue to decoded data
decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS] =
encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 1];
decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1] =
encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 2];
decoded_data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2] =
encoded_epilogue[ELECTRA_ENCODED_EPILOGUE_SIZE - 3];
}
static bool electra_can_be_decoded(
const uint8_t* encoded_base_data,
const uint8_t encoded_base_data_size,
const uint8_t* encoded_epilogue_data,
const uint8_t encoded_epilogue_data_size) {
furi_check(encoded_base_data_size >= ELECTRA_ENCODED_BASE_DATA_SIZE);
furi_check(encoded_epilogue_data_size >= ELECTRA_ENCODED_EPILOGUE_SIZE);
const ElectraDecodedData* base_data = (ElectraDecodedData*)encoded_base_data;
const ElectraDecodedData* epilogue = (ElectraDecodedData*)encoded_epilogue_data;
// check electra epilogue. if em4100 header - break
if((*epilogue & EM_ENCODED_DATA_HEADER) == EM_ENCODED_DATA_HEADER) return false;
// check header and stop bit
if((*base_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
// check row parity
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
uint8_t parity_sum = 0;
for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
parity_sum += (*base_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
}
if((parity_sum % 2)) {
return false;
}
}
// check columns parity
for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
uint8_t parity_sum = 0;
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
parity_sum += (*base_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
}
if((parity_sum % 2)) {
FURI_LOG_D(
TAG,
"Unexpected column parity found. EM4100 data: %016llX",
bit_lib_bytes_to_num_be(encoded_base_data, encoded_base_data_size));
return false;
}
}
// encoded_epilogue_data lsb encoded
uint8_t epilogue_filler = encoded_epilogue_data[(ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2];
for(uint8_t i = 0; i < ((ELECTRA_ENCODED_EPILOGUE_SIZE - 1) - 2); i++)
if(encoded_epilogue_data[i] != epilogue_filler) {
FURI_LOG_D(TAG, "Unexpected epilogue filler found: %016llX", *epilogue);
return false;
}
return true;
}
void protocol_electra_decoder_start(ProtocolElectra* proto) {
memset(proto->data, 0, ELECTRA_DECODED_DATA_SIZE);
proto->encoded_base_data = 0;
proto->encoded_epilogue = 0;
manchester_advance(
proto->decoder_manchester_state,
ManchesterEventReset,
&proto->decoder_manchester_state,
NULL);
};
bool protocol_electra_decoder_feed(ProtocolElectra* proto, bool level, uint32_t duration) {
bool result = false;
ManchesterEvent event = ManchesterEventReset;
if(duration > ELECTRA_READ_SHORT_TIME_LOW && duration < ELECTRA_READ_SHORT_TIME_HIGH) {
if(!level) {
event = ManchesterEventShortHigh;
} else {
event = ManchesterEventShortLow;
}
} else if(duration > ELECTRA_READ_LONG_TIME_LOW && duration < ELECTRA_READ_LONG_TIME_HIGH) {
if(!level) {
event = ManchesterEventLongHigh;
} else {
event = ManchesterEventLongLow;
}
}
if(event != ManchesterEventReset) {
bool data;
bool data_ok = manchester_advance(
proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);
if(data_ok) {
/*
EM 4100 BASE DATA (64 bit) ELECTRA EPILOGUE (64 bit)
_________________________________ _________________________________
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | <- new data bit
--------------------------------- ---------------------------------
<- epilogue msb is carry bit to base data
*/
bool carry = proto->encoded_epilogue >> 63 & 0b1;
proto->encoded_base_data = (proto->encoded_base_data << 1) | carry;
proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data;
if(electra_can_be_decoded(
(uint8_t*)&proto->encoded_base_data,
ELECTRA_ENCODED_BASE_DATA_SIZE,
(uint8_t*)&proto->encoded_epilogue,
ELECTRA_ENCODED_EPILOGUE_SIZE)) {
electra_decode(
(uint8_t*)&proto->encoded_base_data,
ELECTRA_ENCODED_BASE_DATA_SIZE,
(uint8_t*)&proto->encoded_epilogue,
ELECTRA_ENCODED_EPILOGUE_SIZE,
proto->data,
ELECTRA_DECODED_DATA_SIZE);
result = true;
}
}
}
return result;
};
static void em_write_nibble(bool low_nibble, uint8_t data, ElectraDecodedData* encoded_base_data) {
uint8_t parity_sum = 0;
uint8_t start = 0;
if(!low_nibble) start = 4;
for(int8_t i = (start + 3); i >= start; i--) {
parity_sum += (data >> i) & 1;
*encoded_base_data = (*encoded_base_data << 1) | ((data >> i) & 1);
}
*encoded_base_data = (*encoded_base_data << 1) | ((parity_sum % 2) & 1);
}
bool protocol_electra_encoder_start(ProtocolElectra* proto) {
// header
proto->encoded_base_data = 0b111111111;
// data
for(uint8_t i = 0; i < ELECTRA_DECODED_BASE_DATA_SIZE; i++) {
em_write_nibble(false, proto->data[i], &proto->encoded_base_data);
em_write_nibble(true, proto->data[i], &proto->encoded_base_data);
}
// column parity and stop bit
uint8_t parity_sum;
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
parity_sum = 0;
for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
uint8_t parity_bit = (proto->encoded_base_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
parity_sum += parity_bit;
}
proto->encoded_base_data = (proto->encoded_base_data << 1) | ((parity_sum % 2) & 1);
}
// stop bit
proto->encoded_base_data = (proto->encoded_base_data << 1) | 0;
proto->encoded_data_index = 0;
proto->encoded_polarity = true;
// epilogue
proto->encoded_epilogue = (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS]);
proto->encoded_epilogue <<= 8;
proto->encoded_epilogue |= (proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 1]);
//fill bytes 2-7 by epilogue filler
for(uint8_t i = 2; i < ELECTRA_ENCODED_EPILOGUE_SIZE; i++) {
proto->encoded_epilogue <<= 8;
proto->encoded_epilogue |= proto->data[ELECTRA_DECODED_DATA_EPILOGUE_START_POS + 2];
}
return true;
};
LevelDuration protocol_electra_encoder_yield(ProtocolElectra* proto) {
bool level;
if(proto->encoded_data_index < 64)
level = (proto->encoded_base_data >> (63 - proto->encoded_data_index)) & 1;
else
level = (proto->encoded_epilogue >> (63 - (proto->encoded_data_index - 64))) & 1;
uint32_t duration = ELECTRA_CLOCK_PER_BIT / 2;
if(proto->encoded_polarity) {
proto->encoded_polarity = false;
} else {
level = !level;
proto->encoded_polarity = true;
proto->encoded_data_index++;
if(proto->encoded_data_index >= 128) {
proto->encoded_data_index = 0;
}
}
return level_duration_make(level, duration);
};
bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_electra_encoder_start(protocol);
electra_decode(
(uint8_t*)&protocol->encoded_base_data,
sizeof(ElectraDecodedData),
(uint8_t*)&protocol->encoded_epilogue,
sizeof(ElectraDecodedData),
protocol->data,
ELECTRA_DECODED_DATA_SIZE);
protocol_electra_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
request->t5577.block[0] =
(LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 |
(4 << LFRFID_T5577_MAXBLOCK_SHIFT));
request->t5577.block[1] = protocol->encoded_base_data >> 32;
request->t5577.block[2] = protocol->encoded_base_data & 0xFFFFFFFF;
request->t5577.block[3] = protocol->encoded_epilogue >> 32;
request->t5577.block[4] = protocol->encoded_epilogue & 0xFFFFFFFF;
request->t5577.blocks_to_write = 5;
result = true;
}
return result;
};
void protocol_electra_render_data(ProtocolElectra* protocol, FuriString* result) {
furi_string_printf(result, "Epilogue: %016llX", protocol->encoded_epilogue);
};
const ProtocolBase protocol_electra = {
.name = "Electra",
.manufacturer = "Electra",
.data_size = ELECTRA_DECODED_DATA_SIZE,
.features = LFRFIDFeatureASK | LFRFIDFeaturePSK,
.validate_count = 3,
.alloc = (ProtocolAlloc)protocol_electra_alloc,
.free = (ProtocolFree)protocol_electra_free,
.get_data = (ProtocolGetData)protocol_electra_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_electra_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_electra_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_electra_encoder_start,
.yield = (ProtocolEncoderYield)protocol_electra_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_electra_render_data,
.render_brief_data = (ProtocolRenderData)protocol_electra_render_data,
.write_data = (ProtocolWriteData)protocol_electra_write_data,
};

View File

@@ -0,0 +1,4 @@
#pragma once
#include <toolbox/protocols/protocol.h>
extern const ProtocolBase protocol_electra;

View File

@@ -4,6 +4,7 @@
#include "lfrfid_protocols.h"
typedef uint64_t EM4100DecodedData;
typedef uint64_t EM4100Epilogue;
#define EM_HEADER_POS (55)
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
@@ -28,10 +29,13 @@ typedef uint64_t EM4100DecodedData;
#define EM_READ_LONG_TIME_BASE (512)
#define EM_READ_JITTER_TIME_BASE (100)
#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)
typedef struct {
uint8_t data[EM4100_DECODED_DATA_SIZE];
EM4100DecodedData encoded_data;
EM4100Epilogue encoded_epilogue;
uint8_t encoded_data_index;
bool encoded_polarity;
@@ -147,9 +151,16 @@ static void em4100_decode(
}
}
static bool em4100_can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
static bool em4100_can_be_decoded(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
const uint8_t* encoded_epilogue) {
furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE);
const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data;
const EM4100Epilogue* epilogue = (EM4100Epilogue*)encoded_epilogue;
// check first 9 bytes on epilogue (to prevent conflict with Electra protocol)
if((*epilogue & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) return false;
// check header and stop bit
if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
@@ -221,9 +232,15 @@ bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t du
proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data);
if(data_ok) {
proto->encoded_data = (proto->encoded_data << 1) | data;
bool carry = proto->encoded_epilogue >> 63 & 0b1;
if(em4100_can_be_decoded((uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData))) {
proto->encoded_data = (proto->encoded_data << 1) | carry;
proto->encoded_epilogue = (proto->encoded_epilogue << 1) | data;
if(em4100_can_be_decoded(
(uint8_t*)&proto->encoded_data,
sizeof(EM4100DecodedData),
(uint8_t*)&proto->encoded_epilogue)) {
em4100_decode(
(uint8_t*)&proto->encoded_data,
sizeof(EM4100DecodedData),

View File

@@ -21,7 +21,6 @@ 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"),
@@ -34,7 +33,6 @@ 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/emv/emv_poller.h"),
File("protocols/st25tb/st25tb_poller.h"),

View File

@@ -1,189 +0,0 @@
#include "mf_plus_i.h"
#include <bit_lib/bit_lib.h>
#include <furi.h>
#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;
}

View File

@@ -1,117 +0,0 @@
#pragma once
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#include <lib/toolbox/simple_array.h>
#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

View File

@@ -1,216 +0,0 @@
#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;
}

View File

@@ -1,29 +0,0 @@
#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);

View File

@@ -1,367 +0,0 @@
#include "mf_plus_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#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,
};

View File

@@ -1,55 +0,0 @@
#pragma once
#include "mf_plus.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#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

View File

@@ -1,5 +0,0 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase mf_plus_poller;

View File

@@ -1,73 +0,0 @@
#include "mf_plus_poller_i.h"
#include <furi.h>
#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;
}

View File

@@ -1,54 +0,0 @@
#pragma once
#include "mf_plus_poller.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
#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

View File

@@ -20,7 +20,6 @@
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_classic/mf_classic.h>
#include <nfc/protocols/mf_plus/mf_plus.h>
#include <nfc/protocols/mf_desfire/mf_desfire.h>
#include <nfc/protocols/emv/emv.h>
#include <nfc/protocols/slix/slix_device_defs.h>
@@ -41,7 +40,6 @@ 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,

View File

@@ -8,7 +8,6 @@
#include <nfc/protocols/felica/felica_poller_defs.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_defs.h>
#include <nfc/protocols/mf_plus/mf_plus_poller_defs.h>
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
#include <nfc/protocols/emv/emv_poller_defs.h>
#include <nfc/protocols/slix/slix_poller_defs.h>
@@ -23,7 +22,6 @@ 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,

View File

@@ -63,7 +63,6 @@ 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,
};
@@ -132,12 +131,6 @@ 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,

View File

@@ -184,7 +184,6 @@ typedef enum {
NfcProtocolFelica,
NfcProtocolMfUltralight,
NfcProtocolMfClassic,
NfcProtocolMfPlus,
NfcProtocolMfDesfire,
NfcProtocolSlix,
NfcProtocolSt25tb,

View File

@@ -167,8 +167,6 @@ 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,,
@@ -2604,19 +2602,6 @@ 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*
@@ -4250,7 +4235,6 @@ 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,
1 entry status name type params
167 Header + lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h
168 Header + lib/nfc/protocols/mf_desfire/mf_desfire.h
169 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
170 Header + lib/nfc/protocols/mf_ultralight/mf_ultralight.h
171 Header + lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h
172 Header + lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h
2602 Function + mf_desfire_send_chunks MfDesfireError MfDesfirePoller*, const BitBuffer*, BitBuffer*
2603 Function + mf_desfire_set_uid _Bool MfDesfireData*, const uint8_t*, size_t
2604 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*
2605 Function + mf_ultralight_alloc MfUltralightData*
2606 Function + mf_ultralight_copy void MfUltralightData*, const MfUltralightData*
2607 Function + mf_ultralight_detect_protocol _Bool const Iso14443_3aData*
4235 Variable - nfc_device_felica const NfcDeviceBase
4236 Variable - nfc_device_mf_classic const NfcDeviceBase
4237 Variable - nfc_device_mf_desfire const NfcDeviceBase
Variable - nfc_device_mf_plus const NfcDeviceBase
4238 Variable - nfc_device_mf_ultralight const NfcDeviceBase
4239 Variable - nfc_device_st25tb const NfcDeviceBase
4240 Variable + sequence_audiovisual_alert const NotificationSequence