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 53218bb9f..359599ae4 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 @@ -204,7 +204,14 @@ 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_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index 2960886f7..b4d9fee13 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" @@ -82,17 +87,143 @@ 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); + const MfPlusData* mf_plus_data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfPlus); - 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); + // 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 | NfcProtocolFeatureMoreInfo, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index e90444a2a..453396411 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -16,7 +16,8 @@ typedef struct { static const uint32_t mf_classic_data_format_version = 2; -#define MF_CLASSIC_PLUS2K_SCAN_SECTORS 18 +// 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] = diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c index 1f5eea271..72cf7ecbc 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;