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:
Kevin Wallace
2022-03-23 06:45:37 -07:00
committed by GitHub
parent d075e00ae1
commit 3857cd7d5f
26 changed files with 1941 additions and 14 deletions

248
applications/nfc/nfc_worker.c Executable file → Normal file
View 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) {