mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
upd pr 4316
with features flags fix todo: proper workaround for NfcProtocolFeatureEmulateUid
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
#include "mf_plus_render.h"
|
||||
|
||||
#include <nfc/protocols/mf_plus/mf_plus_poller.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <lib/bit_lib/bit_lib.h>
|
||||
|
||||
#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 =
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user