0d5d4c8688
* nfc: Add NTAG I2C (Plus) 1K/2K read support * nfc: Add rudimentary NTAG I2C emulation * nfc: Closer NTAG I2C emulation plus debug logging * nfc: Fix NTAG I2C sector select emulation * nfc: Add security for NTAG I2C * nfc: Send NAK correctly for MFUL reads * nfc: Better emulate NTAG I2C SECTOR_SELECT behavior * nfc: Fix non-I2C Ultralight read Per datasheet, max sector for SECTOR_SELECT is 0xfe, so 0xff is OK as uninit value * nfc: Only read sig for NTAG if supported Attempting to read signature breaks immediate call to sector select on NTAG I2C original for some reason, so don't read signature if the command is not supported Co-authored-by: gornekich <n.gorbadey@gmail.com>
711 lines
29 KiB
C
711 lines
29 KiB
C
#include "nfc_worker_i.h"
|
|
#include <furi_hal.h>
|
|
|
|
#include <lib/nfc_protocols/nfc_util.h>
|
|
#include <lib/nfc_protocols/emv.h>
|
|
#include <lib/nfc_protocols/mifare_common.h>
|
|
#include <lib/nfc_protocols/mifare_ultralight.h>
|
|
#include <lib/nfc_protocols/mifare_classic.h>
|
|
#include <lib/nfc_protocols/mifare_desfire.h>
|
|
#include <lib/nfc_protocols/nfca.h>
|
|
|
|
#include "helpers/nfc_mf_classic_dict.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);
|
|
|
|
return nfc_worker;
|
|
}
|
|
|
|
void nfc_worker_free(NfcWorker* nfc_worker) {
|
|
furi_assert(nfc_worker);
|
|
furi_thread_free(nfc_worker->thread);
|
|
furi_record_close("storage");
|
|
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 = {};
|
|
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 = {};
|
|
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 = {};
|
|
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 = {};
|
|
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 = {};
|
|
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 = {};
|
|
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;
|
|
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) {
|
|
ReturnCode err;
|
|
uint8_t tx_buff[64] = {};
|
|
uint16_t tx_len = 0;
|
|
uint8_t rx_buff[512] = {};
|
|
uint16_t rx_len;
|
|
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_len = mf_df_prepare_get_version(tx_buff);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err);
|
|
continue;
|
|
}
|
|
if(!mf_df_parse_get_version_response(rx_buff, rx_len, &data->version)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response");
|
|
continue;
|
|
}
|
|
|
|
tx_len = mf_df_prepare_get_free_memory(tx_buff);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err == ERR_NONE) {
|
|
data->free_memory = malloc(sizeof(MifareDesfireFreeMemory));
|
|
memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory));
|
|
if(!mf_df_parse_get_free_memory_response(rx_buff, rx_len, 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_len = mf_df_prepare_get_key_settings(tx_buff);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err);
|
|
} else {
|
|
data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings));
|
|
memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings));
|
|
if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, data->master_key_settings)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
|
|
free(data->master_key_settings);
|
|
data->master_key_settings = NULL;
|
|
continue;
|
|
}
|
|
|
|
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_len = mf_df_prepare_get_key_version(tx_buff, key_id);
|
|
err =
|
|
furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err);
|
|
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(rx_buff, rx_len, 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_len = mf_df_prepare_get_application_ids(tx_buff);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err);
|
|
} else {
|
|
if(!mf_df_parse_get_application_ids_response(rx_buff, rx_len, &data->app_head)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response");
|
|
}
|
|
}
|
|
|
|
for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
|
|
tx_len = mf_df_prepare_select_application(tx_buff, app->id);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(!mf_df_parse_select_application_response(rx_buff, rx_len)) {
|
|
FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err);
|
|
continue;
|
|
}
|
|
tx_len = mf_df_prepare_get_key_settings(tx_buff);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err);
|
|
} else {
|
|
app->key_settings = malloc(sizeof(MifareDesfireKeySettings));
|
|
memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings));
|
|
if(!mf_df_parse_get_key_settings_response(rx_buff, rx_len, 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_len = mf_df_prepare_get_key_version(tx_buff, key_id);
|
|
err = furi_hal_nfc_exchange_full(
|
|
tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err);
|
|
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(rx_buff, rx_len, 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_len = mf_df_prepare_get_file_ids(tx_buff);
|
|
err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err);
|
|
} else {
|
|
if(!mf_df_parse_get_file_ids_response(rx_buff, rx_len, &app->file_head)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response");
|
|
}
|
|
}
|
|
|
|
for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
|
|
tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id);
|
|
err =
|
|
furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err);
|
|
continue;
|
|
}
|
|
if(!mf_df_parse_get_file_settings_response(rx_buff, rx_len, file)) {
|
|
FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response");
|
|
continue;
|
|
}
|
|
switch(file->type) {
|
|
case MifareDesfireFileTypeStandard:
|
|
case MifareDesfireFileTypeBackup:
|
|
tx_len = mf_df_prepare_read_data(tx_buff, file->id, 0, 0);
|
|
break;
|
|
case MifareDesfireFileTypeValue:
|
|
tx_len = mf_df_prepare_get_value(tx_buff, file->id);
|
|
break;
|
|
case MifareDesfireFileTypeLinearRecord:
|
|
case MifareDesfireFileTypeCyclicRecord:
|
|
tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0);
|
|
break;
|
|
}
|
|
err =
|
|
furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
|
|
if(err != ERR_NONE) {
|
|
FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err);
|
|
continue;
|
|
}
|
|
if(!mf_df_parse_read_data_response(rx_buff, rx_len, 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;
|
|
}
|
|
}
|