|
2 | 2 | #include "mf_plus_render.h" |
3 | 3 |
|
4 | 4 | #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> |
5 | 9 |
|
6 | 10 | #include "nfc/nfc_app_i.h" |
| 11 | +#include "../../mf_classic_key_cache.h" |
7 | 12 |
|
8 | 13 | #include "../nfc_protocol_support_common.h" |
9 | 14 | #include "../nfc_protocol_support_gui_common.h" |
@@ -82,17 +87,143 @@ static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) { |
82 | 87 | } |
83 | 88 |
|
84 | 89 | 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); |
87 | 135 |
|
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 | + } |
92 | 223 | } |
93 | 224 |
|
94 | 225 | const NfcProtocolSupportBase nfc_protocol_support_mf_plus = { |
95 | | - .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, |
| 226 | + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, |
96 | 227 |
|
97 | 228 | .scene_info = |
98 | 229 | { |
|
0 commit comments