Skip to content

Commit 9d325c6

Browse files
committed
upd pr 4316
with features flags fix todo: proper workaround for NfcProtocolFeatureEmulateUid
1 parent 4c33140 commit 9d325c6

File tree

4 files changed

+157
-9
lines changed

4 files changed

+157
-9
lines changed

applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,14 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {
204204
}
205205

206206
static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) {
207-
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
207+
// Use stored data; normalize ATQA/SAK in-place for 4-byte UID to avoid cascade-bit issues
208+
MfClassicData* data =
209+
(MfClassicData*)nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
210+
if(data->iso14443_3a_data && data->iso14443_3a_data->uid_len == 4) {
211+
data->iso14443_3a_data->atqa[0] = 0x04;
212+
data->iso14443_3a_data->atqa[1] = 0x00;
213+
data->iso14443_3a_data->sak = 0x08; // no cascade bit
214+
}
208215
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, data);
209216
nfc_listener_start(instance->listener, NULL, NULL);
210217
}

applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c

Lines changed: 138 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
#include "mf_plus_render.h"
33

44
#include <nfc/protocols/mf_plus/mf_plus_poller.h>
5+
#include <nfc/protocols/mf_classic/mf_classic.h>
6+
7+
#include <string.h>
8+
#include <lib/bit_lib/bit_lib.h>
59

610
#include "nfc/nfc_app_i.h"
11+
#include "../../mf_classic_key_cache.h"
712

813
#include "../nfc_protocol_support_common.h"
914
#include "../nfc_protocol_support_gui_common.h"
@@ -82,17 +87,143 @@ static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) {
8287
}
8388

8489
static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) {
85-
const Iso14443_4aData* iso14443_4a_data =
86-
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
90+
const MfPlusData* mf_plus_data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfPlus);
91+
92+
// For SL1 2K cards, use Classic emulation (compatible with Classic readers)
93+
// MIFARE Plus 2K SL1 emulates as Classic with 32 sectors (128 blocks total)
94+
// This allows UID-only readers (like printers) to work, and exposes all 32 sectors
95+
if(mf_plus_data->security_level == MfPlusSecurityLevel1 &&
96+
mf_plus_data->size == MfPlusSize2K) {
97+
MfClassicData* classic_data = NULL;
98+
99+
// Try to get Classic data if the card was read as Classic
100+
// This ensures we emulate the actual data that was scanned (all sectors, keys, blocks)
101+
const MfClassicData* existing_classic_data = NULL;
102+
// Check if device protocol is Classic (card was read as Classic, not just Plus)
103+
if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) {
104+
existing_classic_data =
105+
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
106+
}
107+
108+
if(existing_classic_data && existing_classic_data->type == MfClassicTypePlus2k) {
109+
// Use the actual Classic data that was read from the card
110+
// This contains all the real sector data, keys, and blocks from the scan
111+
classic_data = mf_classic_alloc();
112+
mf_classic_copy(classic_data, existing_classic_data);
113+
114+
// Ensure sectors 18-31 are treated as uninitialized to match real card behavior
115+
// On real MIFARE Plus 2K SL1 cards, sectors 18-31 are typically empty/uninitialized
116+
// Clear key masks for sectors 18-31 if keys are zero (uninitialized)
117+
for(uint8_t sector = 18; sector < 32; sector++) {
118+
uint8_t sector_trailer_block = sector * 4 + 3;
119+
MfClassicSectorTrailer* sec_tr =
120+
(MfClassicSectorTrailer*)&classic_data->block[sector_trailer_block];
121+
122+
// Check if both keys are zero (uninitialized)
123+
bool key_a_zero = true;
124+
bool key_b_zero = true;
125+
for(int i = 0; i < 6; i++) {
126+
if(sec_tr->key_a.data[i] != 0) key_a_zero = false;
127+
if(sec_tr->key_b.data[i] != 0) key_b_zero = false;
128+
}
129+
130+
// Check if keys were found in original read
131+
bool key_a_found_orig =
132+
mf_classic_is_key_found(existing_classic_data, sector, MfClassicKeyTypeA);
133+
bool key_b_found_orig =
134+
mf_classic_is_key_found(existing_classic_data, sector, MfClassicKeyTypeB);
87135

88-
instance->listener =
89-
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
90-
nfc_listener_start(
91-
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
136+
// Clear key masks if keys are zero (uninitialized) OR if they weren't found in original
137+
// This ensures empty sectors appear as uninitialized, matching real card
138+
// The listener will reject authentication attempts to sectors without keys found
139+
if(key_a_zero || !key_a_found_orig) {
140+
mf_classic_set_key_not_found(classic_data, sector, MfClassicKeyTypeA);
141+
}
142+
if(key_b_zero || !key_b_found_orig) {
143+
mf_classic_set_key_not_found(classic_data, sector, MfClassicKeyTypeB);
144+
}
145+
}
146+
} else {
147+
// No Classic data available - create minimal Classic data from MF Plus
148+
// This is a fallback when card was only read as Plus (without sector data)
149+
classic_data = mf_classic_alloc();
150+
classic_data->type = MfClassicTypePlus2k;
151+
152+
// Initialize key masks to zero (no keys found) - sectors are uninitialized
153+
classic_data->key_a_mask = 0ULL;
154+
classic_data->key_b_mask = 0ULL;
155+
156+
// Copy ISO14443-3A data from MF Plus (UID, ATQA, SAK)
157+
const Iso14443_3aData* iso3_data =
158+
iso14443_4a_get_base_data(mf_plus_data->iso14443_4a_data);
159+
if(iso3_data) {
160+
iso14443_3a_copy(classic_data->iso14443_3a_data, iso3_data);
161+
// Force SL1 Classic view: ATQA 0x0004, SAK 0x08 (no cascade bit)
162+
classic_data->iso14443_3a_data->atqa[0] = 0x04;
163+
classic_data->iso14443_3a_data->atqa[1] = 0x00;
164+
classic_data->iso14443_3a_data->sak = 0x08;
165+
// Ensure 4-byte UID form (the real card uses 4B UID)
166+
if(classic_data->iso14443_3a_data->uid_len > 4) {
167+
classic_data->iso14443_3a_data->uid_len = 4;
168+
}
169+
170+
// Try to load keys from key cache to speed up emulation
171+
// This allows emulation to work faster if keys were previously cached
172+
if(instance->mfc_key_cache) {
173+
size_t uid_len = 0;
174+
const uint8_t* uid = iso14443_3a_get_uid(iso3_data, &uid_len);
175+
if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) {
176+
// Keys loaded from cache - copy them to classic_data
177+
MfClassicDeviceKeys* cached_keys = &instance->mfc_key_cache->keys;
178+
classic_data->key_a_mask = cached_keys->key_a_mask;
179+
classic_data->key_b_mask = cached_keys->key_b_mask;
180+
181+
// Copy cached keys to sector trailers
182+
for(uint8_t sector = 0; sector < 32; sector++) {
183+
if(FURI_BIT(cached_keys->key_a_mask, sector)) {
184+
MfClassicSectorTrailer* sec_tr =
185+
mf_classic_get_sector_trailer_by_sector(classic_data, sector);
186+
sec_tr->key_a = cached_keys->key_a[sector];
187+
mf_classic_set_key_found(
188+
classic_data,
189+
sector,
190+
MfClassicKeyTypeA,
191+
bit_lib_bytes_to_num_be(cached_keys->key_a[sector].data, 6));
192+
}
193+
if(FURI_BIT(cached_keys->key_b_mask, sector)) {
194+
MfClassicSectorTrailer* sec_tr =
195+
mf_classic_get_sector_trailer_by_sector(classic_data, sector);
196+
sec_tr->key_b = cached_keys->key_b[sector];
197+
mf_classic_set_key_found(
198+
classic_data,
199+
sector,
200+
MfClassicKeyTypeB,
201+
bit_lib_bytes_to_num_be(cached_keys->key_b[sector].data, 6));
202+
}
203+
}
204+
}
205+
}
206+
}
207+
// Note: Without Classic data, sectors without cached keys are uninitialized (no keys found)
208+
// This matches real card behavior for empty sectors
209+
}
210+
211+
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, classic_data);
212+
nfc_listener_start(instance->listener, NULL, NULL);
213+
} else {
214+
// For SL2/SL3, use ISO14443-4A emulation
215+
const Iso14443_4aData* iso14443_4a_data =
216+
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
217+
218+
instance->listener =
219+
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
220+
nfc_listener_start(
221+
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
222+
}
92223
}
93224

94225
const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
95-
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
226+
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
96227

97228
.scene_info =
98229
{

lib/nfc/protocols/mf_classic/mf_classic.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ typedef struct {
1616

1717
static const uint32_t mf_classic_data_format_version = 2;
1818

19-
#define MF_CLASSIC_PLUS2K_SCAN_SECTORS 18
19+
// MIFARE Plus 2K SL1 has 32 sectors (128 blocks total) per official specification
20+
#define MF_CLASSIC_PLUS2K_SCAN_SECTORS 32
2021

2122
static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = {
2223
[MfClassicTypeMini] =

lib/nfc/protocols/mf_classic/mf_classic_listener.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler(
6565

6666
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
6767

68+
// Reject authentication immediately if keys are not found (uninitialized sector)
69+
// This matches real card behavior where empty sectors reject authentication
70+
// Fast path: check mask directly instead of function call
71+
if(key_type == MfClassicKeyTypeA) {
72+
if(FURI_BIT(instance->data->key_a_mask, sector_num) == 0) break;
73+
} else {
74+
if(FURI_BIT(instance->data->key_b_mask, sector_num) == 0) break;
75+
}
76+
6877
MfClassicSectorTrailer* sec_tr =
6978
mf_classic_get_sector_trailer_by_sector(instance->data, sector_num);
7079
MfClassicKey* key = (key_type == MfClassicKeyTypeA) ? &sec_tr->key_a : &sec_tr->key_b;

0 commit comments

Comments
 (0)