88facf20c1
* nfc: don't give up on reading DESFire card if GET_KEY_SETTINGS fails Some cards are configured to refuse to provide key settings, but still provide other info. For example, Ubiquiti UniFi Protect access cards won't list keys or applications, but will still answer GET_FREE_MEMORY. * nfc: don't show error when saving DESFire card with no applications * nfc: fix DESFire load with 0 applications or no PICC key settings Co-authored-by: Kevin Wallace <git+flipperzero@kevin.wallace.seattle.wa.us> Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
707 lines
29 KiB
C
707 lines
29 KiB
C
#include "nfc_worker_i.h"
|
|
#include <furi_hal.h>
|
|
|
|
#define TAG "NfcWorker"
|
|
|
|
/***************************** NFC Worker API *******************************/
|
|
|
|
NfcWorker* nfc_worker_alloc() {
|
|
NfcWorker* nfc_worker = malloc(sizeof(NfcWorker));
|
|
|
|
// Worker thread attributes
|
|
nfc_worker->thread = furi_thread_alloc();
|
|
furi_thread_set_name(nfc_worker->thread, "NfcWorker");
|
|
furi_thread_set_stack_size(nfc_worker->thread, 8192);
|
|
furi_thread_set_callback(nfc_worker->thread, nfc_worker_task);
|
|
furi_thread_set_context(nfc_worker->thread, nfc_worker);
|
|
|
|
nfc_worker->callback = NULL;
|
|
nfc_worker->context = NULL;
|
|
nfc_worker->storage = furi_record_open("storage");
|
|
|
|
// Initialize rfal
|
|
while(furi_hal_nfc_is_busy()) {
|
|
osDelay(10);
|
|
}
|
|
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
|
|
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
|
nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage);
|
|
}
|
|
|
|
return nfc_worker;
|
|
}
|
|
|
|
void nfc_worker_free(NfcWorker* nfc_worker) {
|
|
furi_assert(nfc_worker);
|
|
|
|
furi_thread_free(nfc_worker->thread);
|
|
|
|
furi_record_close("storage");
|
|
|
|
if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker);
|
|
|
|
free(nfc_worker);
|
|
}
|
|
|
|
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
|
|
return nfc_worker->state;
|
|
}
|
|
|
|
void nfc_worker_start(
|
|
NfcWorker* nfc_worker,
|
|
NfcWorkerState state,
|
|
NfcDeviceData* dev_data,
|
|
NfcWorkerCallback callback,
|
|
void* context) {
|
|
furi_assert(nfc_worker);
|
|
furi_assert(dev_data);
|
|
while(furi_hal_nfc_is_busy()) {
|
|
osDelay(10);
|
|
}
|
|
|
|
nfc_worker->callback = callback;
|
|
nfc_worker->context = context;
|
|
nfc_worker->dev_data = dev_data;
|
|
nfc_worker_change_state(nfc_worker, state);
|
|
furi_thread_start(nfc_worker->thread);
|
|
}
|
|
|
|
void nfc_worker_stop(NfcWorker* nfc_worker) {
|
|
furi_assert(nfc_worker);
|
|
if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) {
|
|
return;
|
|
}
|
|
furi_hal_nfc_stop();
|
|
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
|
|
furi_thread_join(nfc_worker->thread);
|
|
}
|
|
|
|
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
|
|
nfc_worker->state = state;
|
|
}
|
|
|
|
/***************************** NFC Worker Thread *******************************/
|
|
|
|
int32_t nfc_worker_task(void* context) {
|
|
NfcWorker* nfc_worker = context;
|
|
|
|
furi_hal_nfc_exit_sleep();
|
|
|
|
if(nfc_worker->state == NfcWorkerStateDetect) {
|
|
nfc_worker_detect(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateEmulate) {
|
|
nfc_worker_emulate(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateReadEMVApp) {
|
|
nfc_worker_read_emv_app(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateReadEMVData) {
|
|
nfc_worker_read_emv(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
|
nfc_worker_emulate_apdu(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateReadMifareUltralight) {
|
|
nfc_worker_read_mifare_ultralight(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) {
|
|
nfc_worker_emulate_mifare_ul(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
|
|
nfc_worker_mifare_classic_dict_attack(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
|
|
nfc_worker_emulate_mifare_classic(nfc_worker);
|
|
} else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
|
|
nfc_worker_read_mifare_desfire(nfc_worker);
|
|
}
|
|
furi_hal_nfc_sleep();
|
|
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nfc_worker_detect(NfcWorker* nfc_worker) {
|
|
nfc_device_data_clear(nfc_worker->dev_data);
|
|
NfcDeviceData* dev_data = nfc_worker->dev_data;
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
|
|
while(nfc_worker->state == NfcWorkerStateDetect) {
|
|
if(furi_hal_nfc_detect(nfc_data, 1000)) {
|
|
// Process first found device
|
|
if(nfc_data->type == FuriHalNfcTypeA) {
|
|
if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
|
|
dev_data->protocol = NfcDeviceProtocolMifareUl;
|
|
} else if(mf_classic_check_card_type(
|
|
nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
|
|
dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
|
} else if(mf_df_check_card_type(
|
|
nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
|
|
dev_data->protocol = NfcDeviceProtocolMifareDesfire;
|
|
} else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
|
|
dev_data->protocol = NfcDeviceProtocolEMV;
|
|
} else {
|
|
dev_data->protocol = NfcDeviceProtocolUnknown;
|
|
}
|
|
}
|
|
|
|
// Notify caller and exit
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
break;
|
|
}
|
|
furi_hal_nfc_sleep();
|
|
osDelay(100);
|
|
}
|
|
}
|
|
|
|
void nfc_worker_emulate(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
|
|
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
|
|
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
|
|
|
|
while(nfc_worker->state == NfcWorkerStateEmulate) {
|
|
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) {
|
|
if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
|
|
reader_data->size = tx_rx.rx_bits / 8;
|
|
if(reader_data->size > 0) {
|
|
memcpy(reader_data->data, tx_rx.rx_data, reader_data->size);
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
}
|
|
} else {
|
|
FURI_LOG_E(TAG, "Failed to get reader commands");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false);
|
|
EmvApplication emv_app = {};
|
|
NfcDeviceData* result = nfc_worker->dev_data;
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
nfc_device_data_clear(result);
|
|
|
|
while(nfc_worker->state == NfcWorkerStateReadEMVApp) {
|
|
if(furi_hal_nfc_detect(nfc_data, 1000)) {
|
|
// Card was found. Check that it supports EMV
|
|
if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
|
|
result->protocol = NfcDeviceProtocolEMV;
|
|
if(emv_search_application(&tx_rx, &emv_app)) {
|
|
// Notify caller and exit
|
|
result->emv_data.aid_len = emv_app.aid_len;
|
|
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
}
|
|
} else {
|
|
FURI_LOG_W(TAG, "Card doesn't support EMV");
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Can't find any cards");
|
|
}
|
|
furi_hal_nfc_sleep();
|
|
osDelay(20);
|
|
}
|
|
}
|
|
|
|
void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false);
|
|
EmvApplication emv_app = {};
|
|
NfcDeviceData* result = nfc_worker->dev_data;
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
nfc_device_data_clear(result);
|
|
|
|
while(nfc_worker->state == NfcWorkerStateReadEMVData) {
|
|
if(furi_hal_nfc_detect(nfc_data, 1000)) {
|
|
// Card was found. Check that it supports EMV
|
|
if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
|
|
result->protocol = NfcDeviceProtocolEMV;
|
|
if(emv_read_bank_card(&tx_rx, &emv_app)) {
|
|
result->emv_data.number_len = emv_app.card_number_len;
|
|
memcpy(
|
|
result->emv_data.number, emv_app.card_number, result->emv_data.number_len);
|
|
result->emv_data.aid_len = emv_app.aid_len;
|
|
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
|
|
if(emv_app.name_found) {
|
|
memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name));
|
|
}
|
|
if(emv_app.exp_month) {
|
|
result->emv_data.exp_mon = emv_app.exp_month;
|
|
result->emv_data.exp_year = emv_app.exp_year;
|
|
}
|
|
if(emv_app.country_code) {
|
|
result->emv_data.country_code = emv_app.country_code;
|
|
}
|
|
if(emv_app.currency_code) {
|
|
result->emv_data.currency_code = emv_app.currency_code;
|
|
}
|
|
// Notify caller and exit
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
FURI_LOG_W(TAG, "Card doesn't support EMV");
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Can't find any cards");
|
|
}
|
|
furi_hal_nfc_sleep();
|
|
osDelay(20);
|
|
}
|
|
}
|
|
|
|
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
|
|
FuriHalNfcDevData params = {
|
|
.uid = {0xCF, 0x72, 0xd4, 0x40},
|
|
.uid_len = 4,
|
|
.atqa = {0x00, 0x04},
|
|
.sak = 0x20,
|
|
.type = FuriHalNfcTypeA,
|
|
};
|
|
|
|
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
|
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
|
|
FURI_LOG_D(TAG, "POS terminal detected");
|
|
if(emv_card_emulation(&tx_rx)) {
|
|
FURI_LOG_D(TAG, "EMV card emulated");
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Can't find reader");
|
|
}
|
|
furi_hal_nfc_sleep();
|
|
osDelay(20);
|
|
}
|
|
}
|
|
|
|
void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false);
|
|
MfUltralightReader reader = {};
|
|
MfUltralightData data = {};
|
|
NfcDeviceData* result = nfc_worker->dev_data;
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
|
|
while(nfc_worker->state == NfcWorkerStateReadMifareUltralight) {
|
|
if(furi_hal_nfc_detect(nfc_data, 300)) {
|
|
if(nfc_data->type == FuriHalNfcTypeA &&
|
|
mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
|
|
FURI_LOG_D(TAG, "Found Mifare Ultralight tag. Start reading");
|
|
if(mf_ul_read_card(&tx_rx, &reader, &data)) {
|
|
result->protocol = NfcDeviceProtocolMifareUl;
|
|
result->mf_ul_data = data;
|
|
// Notify caller and exit
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
break;
|
|
} else {
|
|
FURI_LOG_D(TAG, "Failed reading Mifare Ultralight");
|
|
}
|
|
} else {
|
|
FURI_LOG_W(TAG, "Tag is not Mifare Ultralight");
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Can't find any tags");
|
|
}
|
|
furi_hal_nfc_sleep();
|
|
osDelay(100);
|
|
}
|
|
}
|
|
|
|
void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
MfUltralightEmulator emulator = {};
|
|
mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data);
|
|
while(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) {
|
|
emulator.auth_success = false;
|
|
if(emulator.data.type >= MfUltralightTypeNTAGI2C1K) {
|
|
// Sector index needs to be reset
|
|
emulator.curr_sector = 0;
|
|
}
|
|
furi_hal_nfc_emulate_nfca(
|
|
nfc_data->uid,
|
|
nfc_data->uid_len,
|
|
nfc_data->atqa,
|
|
nfc_data->sak,
|
|
mf_ul_prepare_emulation_response,
|
|
&emulator,
|
|
5000);
|
|
// Check if data was modified
|
|
if(emulator.data_changed) {
|
|
nfc_worker->dev_data->mf_ul_data = emulator.data;
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
emulator.data_changed = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
|
|
furi_assert(nfc_worker->callback);
|
|
FuriHalNfcTxRxContext tx_rx_ctx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx_ctx, false);
|
|
MfClassicAuthContext auth_ctx = {};
|
|
MfClassicReader reader = {};
|
|
uint64_t curr_key = 0;
|
|
uint16_t curr_sector = 0;
|
|
uint8_t total_sectors = 0;
|
|
NfcWorkerEvent event;
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
|
|
// Open dictionary
|
|
nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage);
|
|
if(!nfc_mf_classic_dict_open_file(nfc_worker->dict_stream)) {
|
|
event = NfcWorkerEventNoDictFound;
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
nfc_mf_classic_dict_close_file(nfc_worker->dict_stream);
|
|
stream_free(nfc_worker->dict_stream);
|
|
return;
|
|
}
|
|
|
|
// Detect Mifare Classic card
|
|
while(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
|
|
if(furi_hal_nfc_detect(nfc_data, 300)) {
|
|
if(mf_classic_get_type(
|
|
nfc_data->uid,
|
|
nfc_data->uid_len,
|
|
nfc_data->atqa[0],
|
|
nfc_data->atqa[1],
|
|
nfc_data->sak,
|
|
&reader)) {
|
|
total_sectors = mf_classic_get_total_sectors_num(&reader);
|
|
if(reader.type == MfClassicType1k) {
|
|
event = NfcWorkerEventDetectedClassic1k;
|
|
} else {
|
|
event = NfcWorkerEventDetectedClassic4k;
|
|
}
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
break;
|
|
}
|
|
} else {
|
|
event = NfcWorkerEventNoCardDetected;
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
}
|
|
}
|
|
|
|
if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
|
|
bool card_removed_notified = false;
|
|
bool card_found_notified = false;
|
|
// Seek for mifare classic keys
|
|
for(curr_sector = 0; curr_sector < total_sectors; curr_sector++) {
|
|
FURI_LOG_I(TAG, "Sector: %d ...", curr_sector);
|
|
event = NfcWorkerEventNewSector;
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector);
|
|
bool sector_key_found = false;
|
|
while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) {
|
|
furi_hal_nfc_sleep();
|
|
if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) {
|
|
if(!card_found_notified) {
|
|
if(reader.type == MfClassicType1k) {
|
|
event = NfcWorkerEventDetectedClassic1k;
|
|
} else {
|
|
event = NfcWorkerEventDetectedClassic4k;
|
|
}
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
card_found_notified = true;
|
|
card_removed_notified = false;
|
|
}
|
|
FURI_LOG_D(
|
|
TAG,
|
|
"Try to auth to sector %d with key %04lx%08lx",
|
|
curr_sector,
|
|
(uint32_t)(curr_key >> 32),
|
|
(uint32_t)curr_key);
|
|
if(mf_classic_auth_attempt(&tx_rx_ctx, &auth_ctx, curr_key)) {
|
|
sector_key_found = true;
|
|
if((auth_ctx.key_a != MF_CLASSIC_NO_KEY) &&
|
|
(auth_ctx.key_b != MF_CLASSIC_NO_KEY))
|
|
break;
|
|
}
|
|
} else {
|
|
// Notify that no tag is availalble
|
|
FURI_LOG_D(TAG, "Can't find tags");
|
|
if(!card_removed_notified) {
|
|
event = NfcWorkerEventNoCardDetected;
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
card_removed_notified = true;
|
|
card_found_notified = false;
|
|
}
|
|
}
|
|
if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break;
|
|
osDelay(1);
|
|
}
|
|
if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break;
|
|
if(sector_key_found) {
|
|
// Notify that keys were found
|
|
if(auth_ctx.key_a != MF_CLASSIC_NO_KEY) {
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"Sector %d key A: %04lx%08lx",
|
|
curr_sector,
|
|
(uint32_t)(auth_ctx.key_a >> 32),
|
|
(uint32_t)auth_ctx.key_a);
|
|
event = NfcWorkerEventFoundKeyA;
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
}
|
|
if(auth_ctx.key_b != MF_CLASSIC_NO_KEY) {
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"Sector %d key B: %04lx%08lx",
|
|
curr_sector,
|
|
(uint32_t)(auth_ctx.key_b >> 32),
|
|
(uint32_t)auth_ctx.key_b);
|
|
event = NfcWorkerEventFoundKeyB;
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
}
|
|
// Add sectors to read sequence
|
|
mf_classic_reader_add_sector(&reader, curr_sector, auth_ctx.key_a, auth_ctx.key_b);
|
|
}
|
|
nfc_mf_classic_dict_reset(nfc_worker->dict_stream);
|
|
}
|
|
}
|
|
|
|
if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
|
|
FURI_LOG_I(TAG, "Found keys to %d sectors. Start reading sectors", reader.sectors_to_read);
|
|
uint8_t sectors_read =
|
|
mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data);
|
|
if(sectors_read) {
|
|
event = NfcWorkerEventSuccess;
|
|
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
|
FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read);
|
|
} else {
|
|
event = NfcWorkerEventFail;
|
|
FURI_LOG_W(TAG, "Failed to read any sector");
|
|
}
|
|
nfc_worker->callback(event, nfc_worker->context);
|
|
}
|
|
|
|
nfc_mf_classic_dict_close_file(nfc_worker->dict_stream);
|
|
stream_free(nfc_worker->dict_stream);
|
|
}
|
|
|
|
void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
MfClassicEmulator emulator = {
|
|
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
|
|
.data = nfc_worker->dev_data->mf_classic_data,
|
|
.data_changed = false,
|
|
};
|
|
NfcaSignal* nfca_signal = nfca_signal_alloc();
|
|
tx_rx.nfca_signal = nfca_signal;
|
|
|
|
while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
|
|
if(furi_hal_nfc_listen(
|
|
nfc_data->uid, nfc_data->uid_len, nfc_data->atqa, nfc_data->sak, true, 300)) {
|
|
mf_classic_emulator(&emulator, &tx_rx);
|
|
}
|
|
}
|
|
if(emulator.data_changed) {
|
|
nfc_worker->dev_data->mf_classic_data = emulator.data;
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
emulator.data_changed = false;
|
|
}
|
|
|
|
nfca_signal_free(nfca_signal);
|
|
}
|
|
|
|
void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
|
|
FuriHalNfcTxRxContext tx_rx = {};
|
|
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false);
|
|
NfcDeviceData* result = nfc_worker->dev_data;
|
|
nfc_device_data_clear(result);
|
|
MifareDesfireData* data = &result->mf_df_data;
|
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
|
|
|
while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
|
|
furi_hal_nfc_sleep();
|
|
if(!furi_hal_nfc_detect(nfc_data, 300)) {
|
|
osDelay(100);
|
|
continue;
|
|
}
|
|
memset(data, 0, sizeof(MifareDesfireData));
|
|
if(nfc_data->type != FuriHalNfcTypeA ||
|
|
!mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
|
|
FURI_LOG_D(TAG, "Tag is not DESFire");
|
|
osDelay(100);
|
|
continue;
|
|
}
|
|
|
|
FURI_LOG_D(TAG, "Found DESFire tag");
|
|
|
|
result->protocol = NfcDeviceProtocolMifareDesfire;
|
|
|
|
// Get DESFire version
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_version(tx_rx.tx_data);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting version");
|
|
continue;
|
|
}
|
|
if(!mf_df_parse_get_version_response(tx_rx.rx_data, tx_rx.rx_bits / 8, &data->version)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response");
|
|
continue;
|
|
}
|
|
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx.tx_data);
|
|
if(furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
data->free_memory = malloc(sizeof(MifareDesfireFreeMemory));
|
|
memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory));
|
|
if(!mf_df_parse_get_free_memory_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, data->free_memory)) {
|
|
FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)");
|
|
free(data->free_memory);
|
|
data->free_memory = NULL;
|
|
}
|
|
}
|
|
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_D(TAG, "Bad exchange getting key settings");
|
|
} else {
|
|
data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings));
|
|
memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings));
|
|
if(!mf_df_parse_get_key_settings_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, data->master_key_settings)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
|
free(data->master_key_settings);
|
|
data->master_key_settings = NULL;
|
|
} else {
|
|
MifareDesfireKeyVersion** key_version_head =
|
|
&data->master_key_settings->key_version_head;
|
|
for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) {
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting key version");
|
|
continue;
|
|
}
|
|
MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion));
|
|
memset(key_version, 0, sizeof(MifareDesfireKeyVersion));
|
|
key_version->id = key_id;
|
|
if(!mf_df_parse_get_key_version_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response");
|
|
free(key_version);
|
|
continue;
|
|
}
|
|
*key_version_head = key_version;
|
|
key_version_head = &key_version->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx.tx_data);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting application IDs");
|
|
} else {
|
|
if(!mf_df_parse_get_application_ids_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, &data->app_head)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response");
|
|
}
|
|
}
|
|
|
|
for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_select_application(tx_rx.tx_data, app->id);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx) ||
|
|
!mf_df_parse_select_application_response(tx_rx.rx_data, tx_rx.rx_bits / 8)) {
|
|
FURI_LOG_W(TAG, "Bad exchange selecting application");
|
|
continue;
|
|
}
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting key settings");
|
|
} else {
|
|
app->key_settings = malloc(sizeof(MifareDesfireKeySettings));
|
|
memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings));
|
|
if(!mf_df_parse_get_key_settings_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, app->key_settings)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
|
free(app->key_settings);
|
|
app->key_settings = NULL;
|
|
continue;
|
|
}
|
|
|
|
MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head;
|
|
for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) {
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting key version");
|
|
continue;
|
|
}
|
|
MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion));
|
|
memset(key_version, 0, sizeof(MifareDesfireKeyVersion));
|
|
key_version->id = key_id;
|
|
if(!mf_df_parse_get_key_version_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response");
|
|
free(key_version);
|
|
continue;
|
|
}
|
|
*key_version_head = key_version;
|
|
key_version_head = &key_version->next;
|
|
}
|
|
}
|
|
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx.tx_data);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting file IDs");
|
|
} else {
|
|
if(!mf_df_parse_get_file_ids_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, &app->file_head)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response");
|
|
}
|
|
}
|
|
|
|
for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx.tx_data, file->id);
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting file settings");
|
|
continue;
|
|
}
|
|
if(!mf_df_parse_get_file_settings_response(
|
|
tx_rx.rx_data, tx_rx.rx_bits / 8, file)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response");
|
|
continue;
|
|
}
|
|
switch(file->type) {
|
|
case MifareDesfireFileTypeStandard:
|
|
case MifareDesfireFileTypeBackup:
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_read_data(tx_rx.tx_data, file->id, 0, 0);
|
|
break;
|
|
case MifareDesfireFileTypeValue:
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_get_value(tx_rx.tx_data, file->id);
|
|
break;
|
|
case MifareDesfireFileTypeLinearRecord:
|
|
case MifareDesfireFileTypeCyclicRecord:
|
|
tx_rx.tx_bits = 8 * mf_df_prepare_read_records(tx_rx.tx_data, file->id, 0, 0);
|
|
break;
|
|
}
|
|
if(!furi_hal_nfc_tx_rx_full(&tx_rx)) {
|
|
FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id);
|
|
continue;
|
|
}
|
|
if(!mf_df_parse_read_data_response(tx_rx.rx_data, tx_rx.rx_bits / 8, file)) {
|
|
FURI_LOG_W(TAG, "Bad response reading file %d", file->id);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notify caller and exit
|
|
if(nfc_worker->callback) {
|
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
|
}
|
|
break;
|
|
}
|
|
}
|