diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c index 763c4dea7e3..ff16cf0a349 100644 --- a/applications/main/nfc/helpers/mf_classic_key_cache.c +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -63,7 +63,7 @@ bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break; if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break; - uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); + uint8_t sector_num = mf_classic_get_scannable_sectors_num(data->type); bool key_save_success = true; for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 5c668d530d3..aafaf04c817 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -191,7 +191,13 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { } static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { - const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + // Use stored data; normalize ATQA/SAK in-place for 4-byte UID to avoid cascade-bit issues + MfClassicData* data = (MfClassicData*)nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + if(data->iso14443_3a_data && data->iso14443_3a_data->uid_len == 4) { + data->iso14443_3a_data->atqa[0] = 0x04; + data->iso14443_3a_data->atqa[1] = 0x00; + data->iso14443_3a_data->sak = 0x08; // no cascade bit + } instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, data); nfc_listener_start(instance->listener, NULL, NULL); } diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c index 0382b3333a1..78e45365ec1 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c @@ -8,7 +8,7 @@ void nfc_render_mf_classic_info( FuriString* str) { nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str); - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_total = mf_classic_get_scannable_sectors_num(data->type); uint8_t keys_total = sectors_total * 2; uint8_t keys_found = 0; uint8_t sectors_read = 0; diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index c7b36e21ea3..2b91ac5f89a 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -2,8 +2,13 @@ #include "mf_plus_render.h" #include +#include + +#include +#include #include "nfc/nfc_app_i.h" +#include "../../mf_classic_key_cache.h" #include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" @@ -68,17 +73,138 @@ static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) { } 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 MfPlusData* mf_plus_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfPlus); + + // For SL1 2K cards, use Classic emulation (compatible with Classic readers) + // MIFARE Plus 2K SL1 emulates as Classic with 32 sectors (128 blocks total) + // This allows UID-only readers (like printers) to work, and exposes all 32 sectors + if(mf_plus_data->security_level == MfPlusSecurityLevel1 && + mf_plus_data->size == MfPlusSize2K) { + MfClassicData* classic_data = NULL; + + // Try to get Classic data if the card was read as Classic + // This ensures we emulate the actual data that was scanned (all sectors, keys, blocks) + const MfClassicData* existing_classic_data = NULL; + // Check if device protocol is Classic (card was read as Classic, not just Plus) + if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) { + existing_classic_data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + } + + if(existing_classic_data && existing_classic_data->type == MfClassicTypePlus2k) { + // Use the actual Classic data that was read from the card + // This contains all the real sector data, keys, and blocks from the scan + classic_data = mf_classic_alloc(); + mf_classic_copy(classic_data, existing_classic_data); + + // Ensure sectors 18-31 are treated as uninitialized to match real card behavior + // On real MIFARE Plus 2K SL1 cards, sectors 18-31 are typically empty/uninitialized + // Clear key masks for sectors 18-31 if keys are zero (uninitialized) + for(uint8_t sector = 18; sector < 32; sector++) { + uint8_t sector_trailer_block = sector * 4 + 3; + MfClassicSectorTrailer* sec_tr = + (MfClassicSectorTrailer*)&classic_data->block[sector_trailer_block]; + + // Check if both keys are zero (uninitialized) + bool key_a_zero = true; + bool key_b_zero = true; + for(int i = 0; i < 6; i++) { + if(sec_tr->key_a.data[i] != 0) key_a_zero = false; + if(sec_tr->key_b.data[i] != 0) key_b_zero = false; + } + + // Check if keys were found in original read + bool key_a_found_orig = mf_classic_is_key_found(existing_classic_data, sector, MfClassicKeyTypeA); + bool key_b_found_orig = mf_classic_is_key_found(existing_classic_data, sector, MfClassicKeyTypeB); + + // Clear key masks if keys are zero (uninitialized) OR if they weren't found in original + // This ensures empty sectors appear as uninitialized, matching real card + // The listener will reject authentication attempts to sectors without keys found + if(key_a_zero || !key_a_found_orig) { + mf_classic_set_key_not_found(classic_data, sector, MfClassicKeyTypeA); + } + if(key_b_zero || !key_b_found_orig) { + mf_classic_set_key_not_found(classic_data, sector, MfClassicKeyTypeB); + } + } + } else { + // No Classic data available - create minimal Classic data from MF Plus + // This is a fallback when card was only read as Plus (without sector data) + classic_data = mf_classic_alloc(); + classic_data->type = MfClassicTypePlus2k; + + // Initialize key masks to zero (no keys found) - sectors are uninitialized + classic_data->key_a_mask = 0ULL; + classic_data->key_b_mask = 0ULL; + + // Copy ISO14443-3A data from MF Plus (UID, ATQA, SAK) + const Iso14443_3aData* iso3_data = + iso14443_4a_get_base_data(mf_plus_data->iso14443_4a_data); + if(iso3_data) { + iso14443_3a_copy(classic_data->iso14443_3a_data, iso3_data); + // Force SL1 Classic view: ATQA 0x0004, SAK 0x08 (no cascade bit) + classic_data->iso14443_3a_data->atqa[0] = 0x04; + classic_data->iso14443_3a_data->atqa[1] = 0x00; + classic_data->iso14443_3a_data->sak = 0x08; + // Ensure 4-byte UID form (the real card uses 4B UID) + if(classic_data->iso14443_3a_data->uid_len > 4) { + classic_data->iso14443_3a_data->uid_len = 4; + } + + // Try to load keys from key cache to speed up emulation + // This allows emulation to work faster if keys were previously cached + if(instance->mfc_key_cache) { + size_t uid_len = 0; + const uint8_t* uid = iso14443_3a_get_uid(iso3_data, &uid_len); + if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) { + // Keys loaded from cache - copy them to classic_data + MfClassicDeviceKeys* cached_keys = &instance->mfc_key_cache->keys; + classic_data->key_a_mask = cached_keys->key_a_mask; + classic_data->key_b_mask = cached_keys->key_b_mask; + + // Copy cached keys to sector trailers + for(uint8_t sector = 0; sector < 32; sector++) { + if(FURI_BIT(cached_keys->key_a_mask, sector)) { + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(classic_data, sector); + sec_tr->key_a = cached_keys->key_a[sector]; + mf_classic_set_key_found( + classic_data, sector, MfClassicKeyTypeA, + bit_lib_bytes_to_num_be(cached_keys->key_a[sector].data, 6)); + } + if(FURI_BIT(cached_keys->key_b_mask, sector)) { + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(classic_data, sector); + sec_tr->key_b = cached_keys->key_b[sector]; + mf_classic_set_key_found( + classic_data, sector, MfClassicKeyTypeB, + bit_lib_bytes_to_num_be(cached_keys->key_b[sector].data, 6)); + } + } + } + } + } + // Note: Without Classic data, sectors without cached keys are uninitialized (no keys found) + // This matches real card behavior for empty sectors + } + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, classic_data); + nfc_listener_start(instance->listener, NULL, NULL); + } else { + // For SL2/SL3, use ISO14443-4A emulation + 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 = NfcProtocolFeatureEmulateUid, + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEmulateFull, .scene_info = { diff --git a/applications/main/nfc/plugins/supported_cards/aime.c b/applications/main/nfc/plugins/supported_cards/aime.c index fd2afe00c24..df6f47f6958 100644 --- a/applications/main/nfc/plugins/supported_cards/aime.c +++ b/applications/main/nfc/plugins/supported_cards/aime.c @@ -52,7 +52,7 @@ static bool aime_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(aime_key, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(aime_key, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/banapass.c b/applications/main/nfc/plugins/supported_cards/banapass.c index ac065c3ac40..6832d43a007 100644 --- a/applications/main/nfc/plugins/supported_cards/banapass.c +++ b/applications/main/nfc/plugins/supported_cards/banapass.c @@ -98,7 +98,7 @@ static bool banapass_read(Nfc* nfc, NfcDevice* device) { MfClassicDeviceKeys keys = {}; // Access Code Read Attempt - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); @@ -116,7 +116,7 @@ static bool banapass_read(Nfc* nfc, NfcDevice* device) { } // Value Block Read Attempt - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); diff --git a/applications/main/nfc/plugins/supported_cards/bip.c b/applications/main/nfc/plugins/supported_cards/bip.c index 8b76c2d0758..19e17ada847 100644 --- a/applications/main/nfc/plugins/supported_cards/bip.c +++ b/applications/main/nfc/plugins/supported_cards/bip.c @@ -89,7 +89,7 @@ static bool bip_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(bip_keys_a[i], sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(bip_keys_b[i], sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/disney_infinity.c b/applications/main/nfc/plugins/supported_cards/disney_infinity.c index a98d39ec2ac..e07d1d426bc 100644 --- a/applications/main/nfc/plugins/supported_cards/disney_infinity.c +++ b/applications/main/nfc/plugins/supported_cards/disney_infinity.c @@ -46,7 +46,7 @@ static bool disney_infinity_read(Nfc* nfc, NfcDevice* device) { if(error != MfClassicErrorNone) break; data->type = type; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { di_key(uid_bytes, &keys.key_a[i]); di_key(uid_bytes, &keys.key_b[i]); FURI_BIT_SET(keys.key_a_mask, i); diff --git a/applications/main/nfc/plugins/supported_cards/hi.c b/applications/main/nfc/plugins/supported_cards/hi.c index a26d2265042..594290ea49b 100644 --- a/applications/main/nfc/plugins/supported_cards/hi.c +++ b/applications/main/nfc/plugins/supported_cards/hi.c @@ -137,7 +137,7 @@ static bool hi_read(Nfc* nfc, NfcDevice* device) { uint8_t keyB[HI_KEY_TO_GEN][KEY_LENGTH]; hi_generate_key(uid, keyA, keyB); - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { if(cfg.keys[i].a == 0x000000000000 && cfg.keys[i].b == 0x000000000000) { cfg.keys[i].a = bit_lib_bytes_to_num_be(keyA[i], KEY_LENGTH); cfg.keys[i].b = bit_lib_bytes_to_num_be(keyB[i], KEY_LENGTH); @@ -145,7 +145,7 @@ static bool hi_read(Nfc* nfc, NfcDevice* device) { } MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/hid.c b/applications/main/nfc/plugins/supported_cards/hid.c index 92f23d10fcd..fd623ea5843 100644 --- a/applications/main/nfc/plugins/supported_cards/hid.c +++ b/applications/main/nfc/plugins/supported_cards/hid.c @@ -52,7 +52,7 @@ static bool hid_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(hid_key, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(hid_key, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/hworld.c b/applications/main/nfc/plugins/supported_cards/hworld.c index 674e7b9550b..23dd8e3ee0b 100644 --- a/applications/main/nfc/plugins/supported_cards/hworld.c +++ b/applications/main/nfc/plugins/supported_cards/hworld.c @@ -116,7 +116,7 @@ static bool hworld_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys standard_keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( hworld_standard_keys[i].a, sizeof(MfClassicKey), standard_keys.key_a[i].data); FURI_BIT_SET(standard_keys.key_a_mask, i); @@ -130,7 +130,7 @@ static bool hworld_read(Nfc* nfc, NfcDevice* device) { FURI_LOG_I(TAG, "Standard card successfully read"); } else { MfClassicDeviceKeys vip_keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( hworld_vip_keys[i].a, sizeof(MfClassicKey), vip_keys.key_a[i].data); FURI_BIT_SET(vip_keys.key_a_mask, i); diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c index 18d08f5331a..92156d5e6fd 100644 --- a/applications/main/nfc/plugins/supported_cards/microel.c +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -127,7 +127,7 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) { } // Save keys generated to stucture - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { if(microel_1k_keys[i].a == 0x000000000000) { microel_1k_keys[i].a = bit_lib_bytes_to_num_be(keyA, KEY_LENGTH); } @@ -136,7 +136,7 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) { } } MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( microel_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c index c29c5f90885..22d9fead18a 100644 --- a/applications/main/nfc/plugins/supported_cards/mizip.c +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -150,7 +150,7 @@ static bool mizip_read(Nfc* nfc, NfcDevice* device) { uint8_t keyB[MIZIP_KEY_TO_GEN][KEY_LENGTH]; mizip_generate_key(uid, keyA, keyB); - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { if(cfg.keys[i].a == 0x000000000000 && cfg.keys[i].b == 0x000000000000) { cfg.keys[i].a = bit_lib_bytes_to_num_be(keyA[i], KEY_LENGTH); cfg.keys[i].b = bit_lib_bytes_to_num_be(keyB[i], KEY_LENGTH); @@ -158,7 +158,7 @@ static bool mizip_read(Nfc* nfc, NfcDevice* device) { } MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 06982e1114b..98c85877e90 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -885,7 +885,7 @@ static bool ndef_mfc_parse(const NfcDevice* device, FuriString* parsed_data) { // Check MADs for what sectors contain NDEF data AIDs bool sectors_with_ndef[MF_CLASSIC_TOTAL_SECTORS_MAX] = {0}; - const size_t sector_count = mf_classic_get_total_sectors_num(data->type); + const size_t sector_count = mf_classic_get_scannable_sectors_num(data->type); const struct { size_t block; uint8_t aid_count; diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index 49bbaebe8ec..b479f5b32f8 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -176,7 +176,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) { } MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c index b5dc0ab86e0..e57e9a785ca 100644 --- a/applications/main/nfc/plugins/supported_cards/skylanders.c +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -124,7 +124,7 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { if(i == 0) { bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); diff --git a/applications/main/nfc/plugins/supported_cards/social_moscow.c b/applications/main/nfc/plugins/supported_cards/social_moscow.c index ed2ee6c1d4e..6edf7307fff 100644 --- a/applications/main/nfc/plugins/supported_cards/social_moscow.c +++ b/applications/main/nfc/plugins/supported_cards/social_moscow.c @@ -148,7 +148,7 @@ static bool social_moscow_read(Nfc* nfc, NfcDevice* device) { if(!social_moscow_get_card_config(&cfg, data->type)) break; MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index bd36d40e5bd..9183e8c0c1c 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -151,7 +151,7 @@ static bool troika_read(Nfc* nfc, NfcDevice* device) { .key_a_mask = 0, .key_b_mask = 0, }; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index 6f348fa6159..75c7dfec3df 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -77,7 +77,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( two_cities_4k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c index 737e5734117..742f5cfe1f0 100644 --- a/applications/main/nfc/plugins/supported_cards/washcity.c +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -101,7 +101,7 @@ static bool washcity_read(Nfc* nfc, NfcDevice* device) { .key_a_mask = 0, .key_b_mask = 0, }; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + for(size_t i = 0; i < mf_classic_get_scannable_sectors_num(data->type); i++) { bit_lib_num_to_bytes_be( washcity_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 024bc5c1ea9..12e468210dd 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -38,7 +38,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) MfClassicPollerModeDictAttackStandard; mfc_event->data->poller_mode.data = mfc_data; instance->nfc_dict_context.sectors_total = - mf_classic_get_total_sectors_num(mfc_data->type); + mf_classic_get_scannable_sectors_num(mfc_data->type); mf_classic_get_read_sectors_and_keys( mfc_data, &instance->nfc_dict_context.sectors_read, diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index b1c5c20c9bc..3955971f5f3 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -16,6 +16,9 @@ typedef struct { static const uint32_t mf_classic_data_format_version = 2; +// MIFARE Plus 2K SL1 has 32 sectors (128 blocks total) per official specification +#define MF_CLASSIC_PLUS2K_SCAN_SECTORS 32 + static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = { [MfClassicTypeMini] = { @@ -31,6 +34,14 @@ static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = { .full_name = "Mifare Classic 1K", .type_name = "1K", }, + [MfClassicTypePlus2k] = + { + // MIFARE Plus 2K SL1 maps like a 2K Classic: 32 sectors x 4 blocks + .sectors_total = 32, + .blocks_total = 128, + .full_name = "Mifare Plus 2K SL1", + .type_name = "Plus 2K", + }, [MfClassicType4k] = { .sectors_total = 40, @@ -387,6 +398,14 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { return mf_classic_features[type].sectors_total; } +uint8_t mf_classic_get_scannable_sectors_num(MfClassicType type) { + uint8_t total = mf_classic_get_total_sectors_num(type); + if((type == MfClassicTypePlus2k) && (total > MF_CLASSIC_PLUS2K_SCAN_SECTORS)) { + return MF_CLASSIC_PLUS2K_SCAN_SECTORS; + } + return total; +} + uint16_t mf_classic_get_total_block_num(MfClassicType type) { furi_check(type < MfClassicTypeNum); return mf_classic_features[type].blocks_total; @@ -606,7 +625,7 @@ void mf_classic_get_read_sectors_and_keys( *sectors_read = 0; *keys_found = 0; - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_total = mf_classic_get_scannable_sectors_num(data->type); for(size_t i = 0; i < sectors_total; i++) { if(mf_classic_is_key_found(data, i, MfClassicKeyTypeA)) { *keys_found += 1; @@ -630,7 +649,7 @@ void mf_classic_get_read_sectors_and_keys( bool mf_classic_is_card_read(const MfClassicData* data) { furi_check(data); - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_total = mf_classic_get_scannable_sectors_num(data->type); uint8_t sectors_read = 0; uint8_t keys_found = 0; mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h index 6ae7a623e0b..282cb882df4 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.h +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -48,6 +48,7 @@ typedef enum { typedef enum { MfClassicTypeMini, MfClassicType1k, + MfClassicTypePlus2k, MfClassicType4k, MfClassicTypeNum, @@ -171,6 +172,8 @@ Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data); uint8_t mf_classic_get_total_sectors_num(MfClassicType type); +uint8_t mf_classic_get_scannable_sectors_num(MfClassicType type); + uint16_t mf_classic_get_total_block_num(MfClassicType type); uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector); diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c index 1f5eea27197..72cf7ecbc66 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -65,6 +65,15 @@ static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler( uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + // Reject authentication immediately if keys are not found (uninitialized sector) + // This matches real card behavior where empty sectors reject authentication + // Fast path: check mask directly instead of function call + if(key_type == MfClassicKeyTypeA) { + if(FURI_BIT(instance->data->key_a_mask, sector_num) == 0) break; + } else { + if(FURI_BIT(instance->data->key_b_mask, sector_num) == 0) break; + } + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(instance->data, sector_num); MfClassicKey* key = (key_type == MfClassicKeyTypeA) ? &sec_tr->key_a : &sec_tr->key_b; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index b2d9b114a6c..cf29307c8b7 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -134,6 +134,22 @@ NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) { instance->state = MfClassicPollerStateStart; instance->current_type_check = MfClassicType4k; FURI_LOG_D(TAG, "4K detected"); + } else { + instance->current_type_check = MfClassicTypePlus2k; + } + } else if(instance->current_type_check == MfClassicTypePlus2k) { + // Second-last block in sector 16, which may exist if said sector is not in SL3 mode + MfClassicError error = + mf_classic_poller_get_nt(instance, 66, MfClassicKeyTypeA, NULL, false); + if(error != MfClassicErrorNone) { + // If sector 16 is locked/SL3, try sector 17 as well before falling back + error = mf_classic_poller_get_nt(instance, 70, MfClassicKeyTypeA, NULL, false); + } + if(error == MfClassicErrorNone) { + instance->data->type = MfClassicTypePlus2k; + instance->state = MfClassicPollerStateStart; + instance->current_type_check = MfClassicType4k; + FURI_LOG_D(TAG, "Plus 2K detected"); } else { instance->current_type_check = MfClassicType1k; } @@ -157,7 +173,7 @@ NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) { NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; - instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type); + instance->sectors_total = mf_classic_get_scannable_sectors_num(instance->data->type); memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext)); instance->mfc_event.type = MfClassicPollerEventTypeRequestMode; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 2bb63b88e49..9529addf208 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,87.1,, +Version,+,87.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -2632,6 +2632,7 @@ Function,+,mf_classic_get_device_name,const char*,"const MfClassicData*, NfcDevi Function,+,mf_classic_get_first_block_num_of_sector,uint8_t,uint8_t Function,+,mf_classic_get_key,MfClassicKey,"const MfClassicData*, uint8_t, MfClassicKeyType" Function,+,mf_classic_get_read_sectors_and_keys,void,"const MfClassicData*, uint8_t*, uint8_t*" +Function,+,mf_classic_get_scannable_sectors_num,uint8_t,MfClassicType Function,+,mf_classic_get_sector_by_block,uint8_t,uint8_t Function,+,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"const MfClassicData*, uint8_t" Function,+,mf_classic_get_sector_trailer_num_by_block,uint8_t,uint8_t