Nfc: add basic Mifare DESFire support (#1024)
* Fix TextBox word wrap behavior * Wrap width is 120 pixels, not 140. (140 is larger than the screen!) * Glyph width already includes spacing; don't add 1 additional px * When starting a new line, include wrapped glyph width in new line_width. * Call canvas_set_font before text_box_insert_endline so that glyph width is calculated using correct font. Previous approach worked somewhat well using default TextBoxFontText but this version is more robust, particularly when using TextBoxFontHex. * Add basic Mifare DESFire reading, file/app browser * Fix build with APP_ARCHIVE=0 * Add bool type to flipper_format * Add ability to save and load DESFire card data * Skip over NfcSceneDeviceInfo when viewing saved DESFire info * mf_df_clear: don't leak master key settings key versions * When opening a DESFire card from Archive, retain UID emulation behavior * rm unnecessary \r\n * show Popup instead of leaving view in bad state * Move NfcReaderRequestData out of union This makes it safe to emulate DESFire/EMV without clobbering card data. * Display saved DESFire cards via NfcSceneDeviceInfo * Display and save file metadata even when contents are missing This can happen when a file doesn't allow unauthenticated reads (see the call to mf_df_parse_read_data_response in nfc_worker.c). Co-authored-by: Kevin Wallace <git+flipperzero@kevin.wallace.seattle.wa.us> Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: gornekich <n.gorbadey@gmail.com>
This commit is contained in:
248
applications/nfc/nfc_worker.c
Executable file → Normal file
248
applications/nfc/nfc_worker.c
Executable file → Normal file
@@ -1,6 +1,7 @@
|
||||
#include "nfc_worker_i.h"
|
||||
#include <furi_hal.h>
|
||||
#include "nfc_protocols/emv_decoder.h"
|
||||
#include "nfc_protocols/mifare_desfire.h"
|
||||
#include "nfc_protocols/mifare_ultralight.h"
|
||||
|
||||
#define TAG "NfcWorker"
|
||||
@@ -94,6 +95,8 @@ int32_t nfc_worker_task(void* context) {
|
||||
nfc_worker_read_mifare_ul(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
|
||||
nfc_worker_emulate_mifare_ul(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
|
||||
nfc_worker_read_mifare_desfire(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateField) {
|
||||
nfc_worker_field(nfc_worker);
|
||||
}
|
||||
@@ -108,6 +111,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
|
||||
rfalNfcDevice* dev_list;
|
||||
rfalNfcDevice* dev;
|
||||
uint8_t dev_cnt;
|
||||
nfc_device_data_clear(nfc_worker->dev_data);
|
||||
NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data;
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateDetect) {
|
||||
@@ -126,6 +130,11 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
|
||||
dev->dev.nfca.sensRes.platformInfo,
|
||||
dev->dev.nfca.selRes.sak)) {
|
||||
result->protocol = NfcDeviceProtocolMifareUl;
|
||||
} else if(mf_df_check_card_type(
|
||||
dev->dev.nfca.sensRes.anticollisionInfo,
|
||||
dev->dev.nfca.sensRes.platformInfo,
|
||||
dev->dev.nfca.selRes.sak)) {
|
||||
result->protocol = NfcDeviceProtocolMifareDesfire;
|
||||
} else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) {
|
||||
result->protocol = NfcDeviceProtocolEMV;
|
||||
} else {
|
||||
@@ -192,6 +201,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
|
||||
uint8_t* rx_buff;
|
||||
uint16_t* rx_len;
|
||||
NfcDeviceData* result = nfc_worker->dev_data;
|
||||
nfc_device_data_clear(result);
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateReadEMVApp) {
|
||||
memset(&emv_app, 0, sizeof(emv_app));
|
||||
@@ -253,6 +263,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
||||
uint8_t* rx_buff;
|
||||
uint16_t* rx_len;
|
||||
NfcDeviceData* result = nfc_worker->dev_data;
|
||||
nfc_device_data_clear(result);
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateReadEMV) {
|
||||
memset(&emv_app, 0, sizeof(emv_app));
|
||||
@@ -516,6 +527,7 @@ void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
|
||||
uint16_t* rx_len;
|
||||
MifareUlDevice mf_ul_read;
|
||||
NfcDeviceData* result = nfc_worker->dev_data;
|
||||
nfc_device_data_clear(result);
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateReadMifareUl) {
|
||||
furi_hal_nfc_deactivate();
|
||||
@@ -658,6 +670,242 @@ void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
|
||||
}
|
||||
}
|
||||
|
||||
ReturnCode nfc_exchange_full(
|
||||
uint8_t* tx_buff,
|
||||
uint16_t tx_len,
|
||||
uint8_t* rx_buff,
|
||||
uint16_t rx_cap,
|
||||
uint16_t* rx_len) {
|
||||
ReturnCode err;
|
||||
uint8_t* part_buff;
|
||||
uint16_t* part_len;
|
||||
|
||||
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len, false);
|
||||
if(*part_len > rx_cap) {
|
||||
return ERR_OVERRUN;
|
||||
}
|
||||
memcpy(rx_buff, part_buff, *part_len);
|
||||
*rx_len = *part_len;
|
||||
while(err == ERR_NONE && rx_buff[0] == 0xAF) {
|
||||
err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len, false);
|
||||
if(*part_len > rx_cap - *rx_len) {
|
||||
return ERR_OVERRUN;
|
||||
}
|
||||
if(*part_len == 0) {
|
||||
return ERR_PROTO;
|
||||
}
|
||||
memcpy(rx_buff + *rx_len, part_buff + 1, *part_len - 1);
|
||||
*rx_buff = *part_buff;
|
||||
*rx_len += *part_len - 1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
|
||||
ReturnCode err;
|
||||
rfalNfcDevice* dev_list;
|
||||
uint8_t dev_cnt = 0;
|
||||
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;
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
|
||||
furi_hal_nfc_deactivate();
|
||||
if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) {
|
||||
osDelay(100);
|
||||
continue;
|
||||
}
|
||||
memset(data, 0, sizeof(MifareDesfireData));
|
||||
if(dev_list[0].type != RFAL_NFC_LISTEN_TYPE_NFCA ||
|
||||
!mf_df_check_card_type(
|
||||
dev_list[0].dev.nfca.sensRes.anticollisionInfo,
|
||||
dev_list[0].dev.nfca.sensRes.platformInfo,
|
||||
dev_list[0].dev.nfca.selRes.sak)) {
|
||||
FURI_LOG_D(TAG, "Tag is not DESFire");
|
||||
osDelay(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Found DESFire tag");
|
||||
|
||||
// Fill non-DESFire result data
|
||||
result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len;
|
||||
result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo;
|
||||
result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo;
|
||||
result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak;
|
||||
result->nfc_data.device = NfcDeviceNfca;
|
||||
result->nfc_data.protocol = NfcDeviceProtocolMifareDesfire;
|
||||
memcpy(result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
|
||||
|
||||
// Get DESFire version
|
||||
tx_len = mf_df_prepare_get_version(tx_buff);
|
||||
err = 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 = 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 = 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;
|
||||
}
|
||||
|
||||
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 = 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 = 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 = 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 = 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;
|
||||
}
|
||||
|
||||
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 = 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 = 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 = 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 = 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(nfc_worker->context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_worker_field(NfcWorker* nfc_worker) {
|
||||
furi_hal_nfc_field_on();
|
||||
while(nfc_worker->state == NfcWorkerStateField) {
|
||||
|
Reference in New Issue
Block a user