diff --git a/CHANGELOG.md b/CHANGELOG.md
index 722a6c715..570dac3e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
* Fix BLE sync, fix possible delay related issues
* Disabled debug and trace logs in the FW binary (apps .fap's are not affected) to free up some flash space for new features
* NFC: Fix typo in SLIX poller (by @WillyJL)
+* NFC: Internal MIFARE Plus cleanup - data-drive the "Add Manually" generator variants and unify the admin-key address mapping into one source of truth; small internal-flash saving, no functional change (by @mishamyte | PR #1035)
----
diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c
index c82152e54..fe0c2f53c 100644
--- a/lib/nfc/helpers/nfc_data_generator.c
+++ b/lib/nfc/helpers/nfc_data_generator.c
@@ -446,15 +446,13 @@ static void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) {
// detection (see mf_plus_type_from_ats): S/X mask their size with 2F 2F and differ by the caps
// byte's SVC bit; SE reveals 21 30. The trailing CRC16 floats between configs -- the SE value is a
// real capture (77 C1), the S/X values are AN10833. EV1/EV2 identify via GetVersion, not the ATS, so
-// they reuse the generic Plus block.
+// they reuse the byte-identical S block.
static const uint8_t mf_plus_ats_hist_se[MF_PLUS_ATS_HIST_LEN] =
{0xC1, 0x05, 0x21, 0x30, 0x00, 0x77, 0xC1};
static const uint8_t mf_plus_ats_hist_s[MF_PLUS_ATS_HIST_LEN] =
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7};
static const uint8_t mf_plus_ats_hist_x[MF_PLUS_ATS_HIST_LEN] =
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6};
-static const uint8_t mf_plus_ats_hist_ev[MF_PLUS_ATS_HIST_LEN] =
- {0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7};
// EV1/EV2 answer GetVersion; fill a plausible response. hw_type low nibble 0x02 = Plus, hw_major
// 0x11/0x22 = EV1/EV2, hw_storage 0x16/0x18 = 2K/4K (matches mf_plus_get_type_from_version). The
@@ -616,79 +614,52 @@ static void nfc_generate_mf_plus(
mf_plus_free(data);
}
-static void nfc_generate_mf_plus_se_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeSE, MfPlusSize1K, mf_plus_ats_hist_se);
+// The 18 MIFARE Plus variants differ only by parameters (UID length, product type, memory size and
+// ATS historical bytes), so they are data-driven from this table rather than a thunk each. Indexed
+// by (type - NfcDataGeneratorTypeMfPlusSE_4b), so the row order must track the enum. EV1/EV2 reuse
+// the S ATS block (byte-identical; those products are identified by GetVersion, not the ATS).
+typedef struct {
+ const char* name;
+ uint8_t uid_len;
+ uint8_t type; // MfPlusType
+ uint8_t size; // MfPlusSize
+ const uint8_t* ats_hist;
+} MfPlusGeneratorConfig;
+
+static const MfPlusGeneratorConfig mf_plus_generator_configs[] = {
+ {"Mifare Plus SE 4byte UID", 4, MfPlusTypeSE, MfPlusSize1K, mf_plus_ats_hist_se},
+ {"Mifare Plus SE 7byte UID", 7, MfPlusTypeSE, MfPlusSize1K, mf_plus_ats_hist_se},
+ {"Mifare Plus S 2K 4byte UID", 4, MfPlusTypeS, MfPlusSize2K, mf_plus_ats_hist_s},
+ {"Mifare Plus S 2K 7byte UID", 7, MfPlusTypeS, MfPlusSize2K, mf_plus_ats_hist_s},
+ {"Mifare Plus S 4K 4byte UID", 4, MfPlusTypeS, MfPlusSize4K, mf_plus_ats_hist_s},
+ {"Mifare Plus S 4K 7byte UID", 7, MfPlusTypeS, MfPlusSize4K, mf_plus_ats_hist_s},
+ {"Mifare Plus X 2K 4byte UID", 4, MfPlusTypeX, MfPlusSize2K, mf_plus_ats_hist_x},
+ {"Mifare Plus X 2K 7byte UID", 7, MfPlusTypeX, MfPlusSize2K, mf_plus_ats_hist_x},
+ {"Mifare Plus X 4K 4byte UID", 4, MfPlusTypeX, MfPlusSize4K, mf_plus_ats_hist_x},
+ {"Mifare Plus X 4K 7byte UID", 7, MfPlusTypeX, MfPlusSize4K, mf_plus_ats_hist_x},
+ {"Mifare Plus EV1 2K 4byte UID", 4, MfPlusTypeEV1, MfPlusSize2K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV1 2K 7byte UID", 7, MfPlusTypeEV1, MfPlusSize2K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV1 4K 4byte UID", 4, MfPlusTypeEV1, MfPlusSize4K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV1 4K 7byte UID", 7, MfPlusTypeEV1, MfPlusSize4K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV2 2K 4byte UID", 4, MfPlusTypeEV2, MfPlusSize2K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV2 2K 7byte UID", 7, MfPlusTypeEV2, MfPlusSize2K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV2 4K 4byte UID", 4, MfPlusTypeEV2, MfPlusSize4K, mf_plus_ats_hist_s},
+ {"Mifare Plus EV2 4K 7byte UID", 7, MfPlusTypeEV2, MfPlusSize4K, mf_plus_ats_hist_s},
+};
+
+_Static_assert(
+ COUNT_OF(mf_plus_generator_configs) ==
+ (size_t)(NfcDataGeneratorTypeMfPlusEV2_4k_7b - NfcDataGeneratorTypeMfPlusSE_4b + 1),
+ "mf_plus_generator_configs must cover every MIFARE Plus generator type");
+
+static bool nfc_data_generator_type_is_mf_plus(NfcDataGeneratorType type) {
+ return type >= NfcDataGeneratorTypeMfPlusSE_4b && type <= NfcDataGeneratorTypeMfPlusEV2_4k_7b;
}
-static void nfc_generate_mf_plus_se_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeSE, MfPlusSize1K, mf_plus_ats_hist_se);
-}
-
-static void nfc_generate_mf_plus_s2k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeS, MfPlusSize2K, mf_plus_ats_hist_s);
-}
-
-static void nfc_generate_mf_plus_s2k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeS, MfPlusSize2K, mf_plus_ats_hist_s);
-}
-
-static void nfc_generate_mf_plus_s4k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeS, MfPlusSize4K, mf_plus_ats_hist_s);
-}
-
-static void nfc_generate_mf_plus_s4k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeS, MfPlusSize4K, mf_plus_ats_hist_s);
-}
-
-static void nfc_generate_mf_plus_x2k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeX, MfPlusSize2K, mf_plus_ats_hist_x);
-}
-
-static void nfc_generate_mf_plus_x2k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeX, MfPlusSize2K, mf_plus_ats_hist_x);
-}
-
-static void nfc_generate_mf_plus_x4k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeX, MfPlusSize4K, mf_plus_ats_hist_x);
-}
-
-static void nfc_generate_mf_plus_x4k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeX, MfPlusSize4K, mf_plus_ats_hist_x);
-}
-
-static void nfc_generate_mf_plus_ev1_2k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeEV1, MfPlusSize2K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev1_2k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeEV1, MfPlusSize2K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev1_4k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeEV1, MfPlusSize4K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev1_4k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeEV1, MfPlusSize4K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev2_2k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeEV2, MfPlusSize2K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev2_2k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeEV2, MfPlusSize2K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev2_4k_4b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 4, MfPlusTypeEV2, MfPlusSize4K, mf_plus_ats_hist_ev);
-}
-
-static void nfc_generate_mf_plus_ev2_4k_7b(NfcDevice* nfc_device) {
- nfc_generate_mf_plus(nfc_device, 7, MfPlusTypeEV2, MfPlusSize4K, mf_plus_ats_hist_ev);
-}
-
-static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = {
+// Handler-based table for the Ultralight/NTAG and Classic types, whose per-variant layouts are
+// bespoke. The MIFARE Plus types are parametric and dispatched from mf_plus_generator_configs, so
+// this table intentionally stops before them.
+static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeMfPlusSE_4b] = {
[NfcDataGeneratorTypeMfUltralight] =
{
.name = "Mifare Ultralight",
@@ -779,101 +750,15 @@ static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = {
.name = "Mifare Classic 4k 7byte UID",
.handler = nfc_generate_mf_classic_4k_7b_uid,
},
- [NfcDataGeneratorTypeMfPlusSE_4b] =
- {
- .name = "Mifare Plus SE 4byte UID",
- .handler = nfc_generate_mf_plus_se_4b,
- },
- [NfcDataGeneratorTypeMfPlusSE_7b] =
- {
- .name = "Mifare Plus SE 7byte UID",
- .handler = nfc_generate_mf_plus_se_7b,
- },
- [NfcDataGeneratorTypeMfPlusS2k_4b] =
- {
- .name = "Mifare Plus S 2K 4byte UID",
- .handler = nfc_generate_mf_plus_s2k_4b,
- },
- [NfcDataGeneratorTypeMfPlusS2k_7b] =
- {
- .name = "Mifare Plus S 2K 7byte UID",
- .handler = nfc_generate_mf_plus_s2k_7b,
- },
- [NfcDataGeneratorTypeMfPlusS4k_4b] =
- {
- .name = "Mifare Plus S 4K 4byte UID",
- .handler = nfc_generate_mf_plus_s4k_4b,
- },
- [NfcDataGeneratorTypeMfPlusS4k_7b] =
- {
- .name = "Mifare Plus S 4K 7byte UID",
- .handler = nfc_generate_mf_plus_s4k_7b,
- },
- [NfcDataGeneratorTypeMfPlusX2k_4b] =
- {
- .name = "Mifare Plus X 2K 4byte UID",
- .handler = nfc_generate_mf_plus_x2k_4b,
- },
- [NfcDataGeneratorTypeMfPlusX2k_7b] =
- {
- .name = "Mifare Plus X 2K 7byte UID",
- .handler = nfc_generate_mf_plus_x2k_7b,
- },
- [NfcDataGeneratorTypeMfPlusX4k_4b] =
- {
- .name = "Mifare Plus X 4K 4byte UID",
- .handler = nfc_generate_mf_plus_x4k_4b,
- },
- [NfcDataGeneratorTypeMfPlusX4k_7b] =
- {
- .name = "Mifare Plus X 4K 7byte UID",
- .handler = nfc_generate_mf_plus_x4k_7b,
- },
- [NfcDataGeneratorTypeMfPlusEV1_2k_4b] =
- {
- .name = "Mifare Plus EV1 2K 4byte UID",
- .handler = nfc_generate_mf_plus_ev1_2k_4b,
- },
- [NfcDataGeneratorTypeMfPlusEV1_2k_7b] =
- {
- .name = "Mifare Plus EV1 2K 7byte UID",
- .handler = nfc_generate_mf_plus_ev1_2k_7b,
- },
- [NfcDataGeneratorTypeMfPlusEV1_4k_4b] =
- {
- .name = "Mifare Plus EV1 4K 4byte UID",
- .handler = nfc_generate_mf_plus_ev1_4k_4b,
- },
- [NfcDataGeneratorTypeMfPlusEV1_4k_7b] =
- {
- .name = "Mifare Plus EV1 4K 7byte UID",
- .handler = nfc_generate_mf_plus_ev1_4k_7b,
- },
- [NfcDataGeneratorTypeMfPlusEV2_2k_4b] =
- {
- .name = "Mifare Plus EV2 2K 4byte UID",
- .handler = nfc_generate_mf_plus_ev2_2k_4b,
- },
- [NfcDataGeneratorTypeMfPlusEV2_2k_7b] =
- {
- .name = "Mifare Plus EV2 2K 7byte UID",
- .handler = nfc_generate_mf_plus_ev2_2k_7b,
- },
- [NfcDataGeneratorTypeMfPlusEV2_4k_4b] =
- {
- .name = "Mifare Plus EV2 4K 4byte UID",
- .handler = nfc_generate_mf_plus_ev2_4k_4b,
- },
- [NfcDataGeneratorTypeMfPlusEV2_4k_7b] =
- {
- .name = "Mifare Plus EV2 4K 7byte UID",
- .handler = nfc_generate_mf_plus_ev2_4k_7b,
- },
};
const char* nfc_data_generator_get_name(NfcDataGeneratorType type) {
furi_check(type < NfcDataGeneratorTypeNum);
+ if(nfc_data_generator_type_is_mf_plus(type)) {
+ return mf_plus_generator_configs[type - NfcDataGeneratorTypeMfPlusSE_4b].name;
+ }
+
return nfc_data_generator[type].name;
}
@@ -881,5 +766,17 @@ void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_devi
furi_check(type < NfcDataGeneratorTypeNum);
furi_check(nfc_device);
+ if(nfc_data_generator_type_is_mf_plus(type)) {
+ const MfPlusGeneratorConfig* config =
+ &mf_plus_generator_configs[type - NfcDataGeneratorTypeMfPlusSE_4b];
+ nfc_generate_mf_plus(
+ nfc_device,
+ config->uid_len,
+ (MfPlusType)config->type,
+ (MfPlusSize)config->size,
+ config->ats_hist);
+ return;
+ }
+
nfc_data_generator[type].handler(nfc_device);
}
diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c
index e541208db..845f70ffd 100644
--- a/lib/nfc/protocols/mf_plus/mf_plus_i.c
+++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c
@@ -444,6 +444,44 @@ const char* mf_plus_get_admin_key_name(MfPlusAdminKeyType type) {
return mf_plus_admin_key_names[type];
}
+// Single source of truth for the admin keys' 0x90xx auth addresses. The enum skips 0x9002 (the
+// SL2->SL3 key we never recover), so this is a table rather than 0x9000 + index. Both the poller
+// (which address to authenticate) and the listener (which key a reader is addressing) derive from it.
+typedef struct {
+ uint16_t address;
+ MfPlusAdminKeyType type;
+} MfPlusAdminKeyAddress;
+
+static const MfPlusAdminKeyAddress mf_plus_admin_key_addresses[] = {
+ {0x9000, MfPlusAdminKeyCardMaster},
+ {0x9001, MfPlusAdminKeyCardConfig},
+ {0x9003, MfPlusAdminKeyL3Switch},
+ {0x9004, MfPlusAdminKeySL1CardAuth},
+};
+
+_Static_assert(
+ COUNT_OF(mf_plus_admin_key_addresses) == MfPlusAdminKeyNum,
+ "mf_plus_admin_key_addresses must cover every admin key type");
+
+uint16_t mf_plus_get_admin_key_address(MfPlusAdminKeyType type) {
+ for(size_t i = 0; i < COUNT_OF(mf_plus_admin_key_addresses); i++) {
+ if(mf_plus_admin_key_addresses[i].type == type) {
+ return mf_plus_admin_key_addresses[i].address;
+ }
+ }
+ furi_crash("Invalid MfPlusAdminKeyType");
+}
+
+bool mf_plus_admin_key_type_from_address(uint16_t address, MfPlusAdminKeyType* type) {
+ for(size_t i = 0; i < COUNT_OF(mf_plus_admin_key_addresses); i++) {
+ if(mf_plus_admin_key_addresses[i].address == address) {
+ *type = mf_plus_admin_key_addresses[i].type;
+ return true;
+ }
+ }
+ return false;
+}
+
// Lock the mask-fits-domain invariants at compile time.
_Static_assert(MfPlusAdminKeyNum <= 8, "admin_key_mask (uint8_t) too small");
_Static_assert(MF_PLUS_CONFIG_BLOCK_NUM <= 8, "config_read_mask (uint8_t) too small");
diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.h b/lib/nfc/protocols/mf_plus/mf_plus_i.h
index 2985fd145..b21d9948e 100644
--- a/lib/nfc/protocols/mf_plus/mf_plus_i.h
+++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h
@@ -50,6 +50,11 @@ void mf_plus_set_admin_key_found(MfPlusData* data, MfPlusAdminKeyType type, cons
void mf_plus_set_config_block_read(MfPlusData* data, uint8_t index, const MfPlusBlock* block);
+// Map an admin key <-> its 0x90xx auth address (single source: mf_plus_admin_key_addresses).
+uint16_t mf_plus_get_admin_key_address(MfPlusAdminKeyType type);
+
+bool mf_plus_admin_key_type_from_address(uint16_t address, MfPlusAdminKeyType* type);
+
// Serialize / parse the SL3 payload (data format version, signature, blocks, keys, config).
// Save writes the payload after the identity metadata; load tolerates legacy dumps that have
// no "Data format version" key (returns true, leaving the SL3 fields zeroed).
diff --git a/lib/nfc/protocols/mf_plus/mf_plus_listener_i.c b/lib/nfc/protocols/mf_plus/mf_plus_listener_i.c
index 7139e0f05..8e5a942c0 100644
--- a/lib/nfc/protocols/mf_plus/mf_plus_listener_i.c
+++ b/lib/nfc/protocols/mf_plus/mf_plus_listener_i.c
@@ -59,22 +59,7 @@ static bool
}
MfPlusAdminKeyType admin_type;
- switch(key_id) {
- case 0x9000:
- admin_type = MfPlusAdminKeyCardMaster;
- break;
- case 0x9001:
- admin_type = MfPlusAdminKeyCardConfig;
- break;
- case 0x9003:
- admin_type = MfPlusAdminKeyL3Switch;
- break;
- case 0x9004:
- admin_type = MfPlusAdminKeySL1CardAuth;
- break;
- default:
- return false;
- }
+ if(!mf_plus_admin_key_type_from_address(key_id, &admin_type)) return false;
if(!mf_plus_is_admin_key_found(data, admin_type)) return false;
*key = data->admin_key[admin_type];
return true;
@@ -291,25 +276,7 @@ static bool mf_plus_listener_store_write(
}
MfPlusAdminKeyType admin_type;
- bool is_admin = true;
- switch(block_addr) {
- case 0x9000:
- admin_type = MfPlusAdminKeyCardMaster;
- break;
- case 0x9001:
- admin_type = MfPlusAdminKeyCardConfig;
- break;
- case 0x9003:
- admin_type = MfPlusAdminKeyL3Switch;
- break;
- case 0x9004:
- admin_type = MfPlusAdminKeySL1CardAuth;
- break;
- default:
- is_admin = false;
- break;
- }
- if(is_admin) {
+ if(mf_plus_admin_key_type_from_address(block_addr, &admin_type)) {
MfPlusKey key;
memcpy(key.data, plain, MF_PLUS_KEY_SIZE);
mf_plus_set_admin_key_found(data, admin_type, &key);
diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.c b/lib/nfc/protocols/mf_plus/mf_plus_poller.c
index 20fa55b07..223d16a5e 100644
--- a/lib/nfc/protocols/mf_plus/mf_plus_poller.c
+++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.c
@@ -151,15 +151,6 @@ static NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) {
return command;
}
-// Auth addresses for the admin keys we recover, indexed by MfPlusAdminKeyType. The enum skips
-// 0x9002 (SL2 switch), so this is not simply 0x9000 + index.
-static const uint16_t mf_plus_admin_key_id[MfPlusAdminKeyNum] = {
- [MfPlusAdminKeyCardMaster] = 0x9000,
- [MfPlusAdminKeyCardConfig] = 0x9001,
- [MfPlusAdminKeyL3Switch] = 0x9003,
- [MfPlusAdminKeySL1CardAuth] = 0x9004,
-};
-
#define MF_PLUS_CONFIG_BLOCK_HIGH (0xB0) // config blocks live at 0xB000..0xB003
// Advance the read scan to the next (sector, key type): key A -> key B for the same sector, then
@@ -438,7 +429,10 @@ static NfcCommand mf_plus_poller_handler_auth_admin_key(MfPlusPoller* instance)
const MfPlusAdminKeyType admin_type = (MfPlusAdminKeyType)instance->current_admin;
MfPlusError error = mf_plus_poller_authenticate_key_id(
- instance, mf_plus_admin_key_id[admin_type], &instance->current_key, &instance->session);
+ instance,
+ mf_plus_get_admin_key_address(admin_type),
+ &instance->current_key,
+ &instance->session);
if(error == MfPlusErrorNone) {
mf_plus_set_admin_key_found(instance->data, admin_type, &instance->current_key);