1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 12:51:22 +04:00

[FL-3750] Mf Desfire multiple file rights support (#3576)

* mf desfire: remove unused type
* mf desfire: continue reading after failed get free mem cmd
* mf desfire: fix processing read master key settings command
* mf desfire: don't read applications if they are auth protected
* mf desfire: handle multiple rights
* mf desfire: fix PVS warnings
* mf desfire: fix print format
* mf desfire: fix logs
* mf classic: add send frame functions to poller
* unit tests: add test from mfc crypto frame exchange
* mf classic: add documentation
* mf classic: fix incorrect name
* target: fix api version
This commit is contained in:
gornekich
2024-04-16 06:55:24 +01:00
committed by GitHub
parent fb9728d570
commit 1a40fae003
14 changed files with 576 additions and 113 deletions

View File

@@ -315,6 +315,89 @@ MfClassicError mf_classic_poller_value_cmd(
*/
MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num);
/**
* @brief Transmit and receive Iso14443_3a standard frames in poller mode.
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer, with a timeout defined by the fwt parameter.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_send_standard_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc);
/**
* @brief Transmit and receive Iso14443_3a frames in poller mode.
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer, with a timeout defined by the fwt parameter.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_send_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc);
/**
* @brief Transmit and receive Iso14443_3a frames with custom parity bits in poller mode.
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer, with a timeout defined by the fwt parameter.
*
* Custom parity bits must be set in the tx_buffer. The rx_buffer will contain
* the received data with the parity bits.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_send_custom_parity_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc);
/**
* @brief Transmit and receive Mifare Classic encrypted frames with custom parity bits in poller mode.
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer, with a timeout defined by the fwt parameter.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the plain data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with decyphered received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_send_encrypted_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc);
#ifdef __cplusplus
}
#endif

View File

@@ -465,3 +465,77 @@ MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8
return ret;
}
MfClassicError mf_classic_poller_send_standard_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc) {
furi_check(instance);
furi_check(tx_buffer);
furi_check(rx_buffer);
Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
return mf_classic_process_error(error);
}
MfClassicError mf_classic_poller_send_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc) {
furi_check(instance);
furi_check(tx_buffer);
furi_check(rx_buffer);
Iso14443_3aError error =
iso14443_3a_poller_txrx(instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
return mf_classic_process_error(error);
}
MfClassicError mf_classic_poller_send_custom_parity_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc) {
furi_check(instance);
furi_check(tx_buffer);
furi_check(rx_buffer);
Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
return mf_classic_process_error(error);
}
MfClassicError mf_classic_poller_send_encrypted_frame(
MfClassicPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc) {
furi_check(instance);
furi_check(tx_buffer);
furi_check(rx_buffer);
MfClassicError ret = MfClassicErrorNone;
do {
crypto1_encrypt(instance->crypto, NULL, tx_buffer, instance->tx_encrypted_buffer);
Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
fwt_fc);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
crypto1_decrypt(instance->crypto, instance->rx_encrypted_buffer, rx_buffer);
} while(false);
return ret;
}

View File

@@ -21,8 +21,6 @@ extern "C" {
#define MF_DESFIRE_CMD_GET_VALUE (0x6C)
#define MF_DESFIRE_CMD_READ_RECORDS (0xBB)
#define MF_DESFIRE_FLAG_HAS_NEXT (0xAF)
#define MF_DESFIRE_MAX_KEYS (14)
#define MF_DESFIRE_MAX_FILES (32)
@@ -71,11 +69,6 @@ typedef struct {
typedef uint8_t MfDesfireKeyVersion;
typedef struct {
MfDesfireKeySettings key_settings;
SimpleArray* key_versions;
} MfDesfireKeyConfiguration;
typedef enum {
MfDesfireFileTypeStandard = 0,
MfDesfireFileTypeBackup = 1,
@@ -96,7 +89,8 @@ typedef uint16_t MfDesfireFileAccessRights;
typedef struct {
MfDesfireFileType type;
MfDesfireFileCommunicationSettings comm;
MfDesfireFileAccessRights access_rights;
MfDesfireFileAccessRights access_rights[MF_DESFIRE_MAX_KEYS];
uint8_t access_rights_len;
union {
struct {
uint32_t size;
@@ -136,6 +130,7 @@ typedef enum {
MfDesfireErrorNotPresent,
MfDesfireErrorProtocol,
MfDesfireErrorTimeout,
MfDesfireErrorAuthentication,
} MfDesfireError;
typedef struct {

View File

@@ -1,5 +1,7 @@
#include "mf_desfire_i.h"
#define TAG "MfDesfire"
#define BITS_IN_BYTE (8U)
#define MF_DESFIRE_FFF_VERSION_KEY \
@@ -175,59 +177,88 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
};
} MfDesfireFileSettingsLayout;
MfDesfireFileSettings file_settings_temp = {};
do {
const size_t data_size = bit_buffer_get_size_bytes(buf);
const uint8_t* data_ptr = bit_buffer_get_data(buf);
const size_t min_data_size =
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData);
const size_t max_data_size =
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue);
if(data_size < min_data_size) break;
if(data_size <= max_data_size) {
MfDesfireFileSettingsLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout));
data->type = layout.header.type;
data->comm = layout.header.comm;
data->access_rights = layout.header.access_rights;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
if(data_size != min_data_size) break;
data->data.size = layout.data.size;
} else if(data->type == MfDesfireFileTypeValue) {
if(data_size !=
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue))
break;
data->value.lo_limit = layout.value.lo_limit;
data->value.hi_limit = layout.value.hi_limit;
data->value.limited_credit_value = layout.value.limited_credit_value;
data->value.limited_credit_enabled = layout.value.limited_credit_enabled;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
if(data_size !=
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord))
break;
data->record.size = layout.record.size;
data->record.max = layout.record.max;
data->record.cur = layout.record.cur;
} else {
break;
}
} else {
// TODO FL-3750: process HID Desfire command response here
// Set default fields for now
data->type = 0;
data->comm = 0;
data->access_rights = 0;
data->data.size = 0;
if(data_size < min_data_size) {
FURI_LOG_E(
TAG, "File settings size %zu less than minimum %zu", data_size, min_data_size);
break;
}
size_t bytes_processed = sizeof(MfDesfireFileSettingsHeader);
MfDesfireFileSettingsLayout layout = {};
memcpy(&layout.header, data_ptr, sizeof(MfDesfireFileSettingsHeader));
bool has_additional_access_rights = (layout.header.comm & 0x80) != 0;
file_settings_temp.type = layout.header.type;
file_settings_temp.comm = layout.header.comm & 0x03;
file_settings_temp.access_rights_len = 1;
file_settings_temp.access_rights[0] = layout.header.access_rights;
if(file_settings_temp.type == MfDesfireFileTypeStandard ||
file_settings_temp.type == MfDesfireFileTypeBackup) {
memcpy(
&layout.data,
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
sizeof(MfDesfireFileSettingsData));
file_settings_temp.data.size = layout.data.size;
bytes_processed += sizeof(MfDesfireFileSettingsData);
} else if(file_settings_temp.type == MfDesfireFileTypeValue) {
memcpy(
&layout.value,
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
sizeof(MfDesfireFileSettingsValue));
file_settings_temp.value.lo_limit = layout.value.lo_limit;
file_settings_temp.value.hi_limit = layout.value.hi_limit;
file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value;
file_settings_temp.value.limited_credit_enabled = layout.value.limited_credit_enabled;
bytes_processed += sizeof(MfDesfireFileSettingsValue);
} else if(
file_settings_temp.type == MfDesfireFileTypeLinearRecord ||
file_settings_temp.type == MfDesfireFileTypeCyclicRecord) {
memcpy(
&layout.record,
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
sizeof(MfDesfireFileSettingsRecord));
file_settings_temp.record.size = layout.record.size;
file_settings_temp.record.max = layout.record.max;
file_settings_temp.record.cur = layout.record.cur;
bytes_processed += sizeof(MfDesfireFileSettingsRecord);
} else {
FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type);
break;
}
if(has_additional_access_rights) {
uint8_t additional_access_rights_len = bit_buffer_get_byte(buf, bytes_processed);
FURI_LOG_D(TAG, "Has additional rights: %d", additional_access_rights_len);
if(data_size != bytes_processed +
additional_access_rights_len * sizeof(MfDesfireFileAccessRights) +
1) {
FURI_LOG_W(TAG, "Unexpected command length: %zu", data_size);
for(size_t i = 0; i < bit_buffer_get_size_bytes(buf); i++) {
printf("%02X ", bit_buffer_get_byte(buf, i));
}
printf("\r\n");
break;
}
if(additional_access_rights_len >
MF_DESFIRE_MAX_KEYS * sizeof(MfDesfireFileAccessRights))
break;
memcpy(
&file_settings_temp.access_rights[1],
&data_ptr[bytes_processed],
additional_access_rights_len * sizeof(MfDesfireFileAccessRights));
file_settings_temp.access_rights_len += additional_access_rights_len;
}
*data = file_settings_temp;
parsed = true;
} while(false);
@@ -392,18 +423,19 @@ bool mf_desfire_file_settings_load(
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);
if(!flipper_format_read_hex(
ff,
furi_string_get_cstr(key),
(uint8_t*)&data->access_rights,
sizeof(MfDesfireFileAccessRights)))
uint32_t access_rights_len = 0;
if(!flipper_format_get_value_count(ff, furi_string_get_cstr(key), &access_rights_len))
break;
if((access_rights_len == 0) || ((access_rights_len % 2) != 0)) break;
if(!flipper_format_read_hex(
ff, furi_string_get_cstr(key), (uint8_t*)&data->access_rights, access_rights_len))
break;
data->access_rights_len = access_rights_len / sizeof(MfDesfireFileAccessRights);
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))
break;
} else if(data->type == MfDesfireFileTypeValue) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))
@@ -641,8 +673,8 @@ bool mf_desfire_file_settings_save(
if(!flipper_format_write_hex(
ff,
furi_string_get_cstr(key),
(const uint8_t*)&data->access_rights,
sizeof(MfDesfireFileAccessRights)))
(const uint8_t*)data->access_rights,
data->access_rights_len * sizeof(MfDesfireFileAccessRights)))
break;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
@@ -737,8 +769,11 @@ bool mf_desfire_application_save(
if(i != key_version_count) break;
const uint32_t file_count = simple_array_get_count(data->file_ids);
if(!mf_desfire_file_ids_save(simple_array_get_data(data->file_ids), file_count, prefix, ff))
break;
if(file_count > 0) {
if(!mf_desfire_file_ids_save(
simple_array_get_data(data->file_ids), file_count, prefix, ff))
break;
}
for(i = 0; i < file_count; ++i) {
const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);

View File

@@ -5,6 +5,52 @@
#define MF_DESFIRE_FFF_PICC_PREFIX "PICC"
#define MF_DESFIRE_FFF_APP_PREFIX "Application"
// Successful operation
#define MF_DESFIRE_STATUS_OPERATION_OK (0x00)
// No changes done to backup files, CommitTransaction / AbortTransaction not necessary
#define MF_DESFIRE_STATUS_NO_CHANGES (0x0C)
// Insufficient NV-Memory to complete command
#define MF_DESFIRE_STATUS_OUT_OF_EEPROM_ERROR (0x0E)
// Command code not supported
#define MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE (0x1C)
// CRC or MAC does not match data Padding bytes not valid
#define MF_DESFIRE_STATUS_INTEGRITY_ERROR (0x1E)
// Invalid key number specified
#define MF_DESFIRE_STATUS_NO_SUCH_KEY (0x40)
// Length of command string invalid
#define MF_DESFIRE_STATUS_LENGTH_ERROR (0x7E)
// Current configuration / status does not allow the requested command
#define MF_DESFIRE_STATUS_PERMISSION_DENIED (0x9D)
// Value of the parameter(s) invalid
#define MF_DESFIRE_STATUS_PARAMETER_ERROR (0x9E)
// Requested AID not present on PICC
#define MF_DESFIRE_STATUS_APPLICATION_NOT_FOUND (0xA0)
// Unrecoverable error within application, application will be disabled
#define MF_DESFIRE_STATUS_APPL_INTEGRITY_ERROR (0xA1)
// Current authentication status does not allow the requested command
#define MF_DESFIRE_STATUS_AUTHENTICATION_ERROR (0xAE)
// Additional data frame is expected to be sent
#define MF_DESFIRE_STATUS_ADDITIONAL_FRAME (0xAF)
// Attempt to read/write data from/to beyond the file's/record's limits
// Attempt to exceed the limits of a value file.
#define MF_DESFIRE_STATUS_BOUNDARY_ERROR (0xBE)
// Unrecoverable error within PICC, PICC will be disabled
#define MF_DESFIRE_STATUS_PICC_INTEGRITY_ERROR (0xC1)
// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD
#define MF_DESFIRE_STATUS_COMMAND_ABORTED (0xCA)
// PICC was disabled by an unrecoverable error
#define MF_DESFIRE_STATUS_PICC_DISABLED_ERROR (0xCD)
// Number of Applications limited to 28, no additional CreateApplication possible
#define MF_DESFIRE_STATUS_COUNT_ERROR (0xCE)
// Creation of file/application failed because file/application with same number already exists
#define MF_DESFIRE_STATUS_DUBLICATE_ERROR (0xDE)
// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated
#define MF_DESFIRE_STATUS_EEPROM_ERROR (0xEE)
// Specified file number does not exist
#define MF_DESFIRE_STATUS_FILE_NOT_FOUND (0xF0)
// Unrecoverable error within file, file will be disabled
#define MF_DESFIRE_STATUS_FILE_INTEGRITY_ERROR (0xF1)
// SimpleArray configurations
extern const SimpleArrayConfig mf_desfire_key_version_array_config;

View File

@@ -75,17 +75,23 @@ static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instan
}
static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->error = mf_desfire_poller_read_free_memory(instance, &instance->data->free_memory);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read free memory success");
instance->state = MfDesfirePollerStateReadMasterKeySettings;
} else if(instance->error == MfDesfireErrorNotPresent) {
FURI_LOG_D(TAG, "Read free memoty is unsupported");
instance->state = MfDesfirePollerStateReadMasterKeySettings;
command = NfcCommandReset;
} else {
FURI_LOG_E(TAG, "Failed to read free memory");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
return command;
}
static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) {
@@ -94,6 +100,11 @@ static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePo
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read master key settings success");
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
} else if(instance->error == MfDesfireErrorAuthentication) {
FURI_LOG_D(TAG, "Auth is required to read master key settings and app ids");
instance->data->master_key_settings.is_free_directory_list = false;
instance->data->master_key_settings.max_keys = 1;
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
} else {
FURI_LOG_E(TAG, "Failed to read master key settings");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
@@ -110,7 +121,11 @@ static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePol
instance->data->master_key_settings.max_keys);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read master key version success");
instance->state = MfDesfirePollerStateReadApplicationIds;
if(instance->data->master_key_settings.is_free_directory_list) {
instance->state = MfDesfirePollerStateReadApplicationIds;
} else {
instance->state = MfDesfirePollerStateReadSuccess;
}
} else {
FURI_LOG_E(TAG, "Failed to read master key version");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
@@ -126,6 +141,9 @@ static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read application ids success");
instance->state = MfDesfirePollerStateReadApplications;
} else if(instance->error == MfDesfireErrorAuthentication) {
FURI_LOG_D(TAG, "Read application ids impossible without authentication");
instance->state = MfDesfirePollerStateReadSuccess;
} else {
FURI_LOG_E(TAG, "Failed to read application ids");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);

View File

@@ -19,6 +19,17 @@ MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
}
}
MfDesfireError mf_desfire_process_status_code(uint8_t status_code) {
switch(status_code) {
case MF_DESFIRE_STATUS_OPERATION_OK:
return MfDesfireErrorNone;
case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR:
return MfDesfireErrorAuthentication;
default:
return MfDesfireErrorProtocol;
}
}
MfDesfireError mf_desfire_send_chunks(
MfDesfirePoller* instance,
const BitBuffer* tx_buffer,
@@ -42,7 +53,7 @@ MfDesfireError mf_desfire_send_chunks(
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT);
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME);
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
@@ -50,7 +61,8 @@ MfDesfireError mf_desfire_send_chunks(
bit_buffer_reset(rx_buffer);
}
while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) {
while(
bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
@@ -71,6 +83,11 @@ MfDesfireError mf_desfire_send_chunks(
}
} while(false);
if(error == MfDesfireErrorNone) {
uint8_t err_code = bit_buffer_get_byte(instance->rx_buffer, 0);
error = mf_desfire_process_status_code(err_code);
}
return error;
}
@@ -110,7 +127,7 @@ MfDesfireError
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
error = MfDesfireErrorNotPresent;
}
} while(false);
@@ -414,13 +431,25 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
simple_array_init(data, file_id_count);
}
for(uint32_t i = 0; i < file_id_count; ++i) {
for(size_t i = 0; i < file_id_count; ++i) {
const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i);
const MfDesfireFileType file_type = file_settings_cur->type;
MfDesfireFileData* file_data = simple_array_get(data, i);
bool can_read_data = false;
for(size_t j = 0; j < file_settings_cur->access_rights_len; j++) {
uint8_t read_access = (file_settings_cur->access_rights[j] >> 12) & 0x0f;
uint8_t read_write_access = (file_settings_cur->access_rights[j] >> 4) & 0x0f;
can_read_data = (read_access == 0x0e) || (read_write_access == 0x0e);
if(can_read_data) break;
}
if(!can_read_data) {
FURI_LOG_D(TAG, "Can't read file %zu data without authentication", i);
continue;
}
if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {
error = mf_desfire_poller_read_file_data(
instance, file_id, 0, file_settings_cur->data.size, file_data);
@@ -432,8 +461,6 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
error = mf_desfire_poller_read_file_records(
instance, file_id, 0, file_settings_cur->data.size, file_data);
}
if(error != MfDesfireErrorNone) break;
}
return error;
@@ -448,22 +475,36 @@ MfDesfireError
do {
error = mf_desfire_poller_read_key_settings(instance, &data->key_settings);
if(error == MfDesfireErrorAuthentication) {
FURI_LOG_D(TAG, "Auth is required to read master key settings and app ids");
data->key_settings.is_free_directory_list = false;
error = MfDesfireErrorNone;
break;
}
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_read_key_versions(
instance, data->key_versions, data->key_settings.max_keys);
if(error != MfDesfireErrorNone) break;
if(error != MfDesfireErrorNone) {
FURI_LOG_E(TAG, "Failed to read key version: %d", error);
break;
}
error = mf_desfire_poller_read_file_ids(instance, data->file_ids);
if(error != MfDesfireErrorNone) break;
if(error != MfDesfireErrorNone) {
FURI_LOG_E(TAG, "Failed to read file ids: %d", error);
break;
}
error = mf_desfire_poller_read_file_settings_multi(
instance, data->file_ids, data->file_settings);
if(error != MfDesfireErrorNone) break;
if(error != MfDesfireErrorNone) {
FURI_LOG_E(TAG, "Failed to read file settings: %d", error);
break;
}
error = mf_desfire_poller_read_file_data_multi(
instance, data->file_ids, data->file_settings, data->file_data);
if(error != MfDesfireErrorNone) break;
} while(false);
@@ -484,11 +525,13 @@ MfDesfireError mf_desfire_poller_read_applications(
simple_array_init(data, app_id_count);
}
for(uint32_t i = 0; i < app_id_count; ++i) {
for(size_t i = 0; i < app_id_count; ++i) {
do {
FURI_LOG_D(TAG, "Selecting app %zu", i);
error = mf_desfire_poller_select_application(instance, simple_array_cget(app_ids, i));
if(error != MfDesfireErrorNone) break;
FURI_LOG_D(TAG, "Reading app %zu", i);
MfDesfireApplication* current_app = simple_array_get(data, i);
error = mf_desfire_poller_read_application(instance, current_app);

View File

@@ -47,6 +47,8 @@ struct MfDesfirePoller {
MfDesfireError mf_desfire_process_error(Iso14443_4aError error);
MfDesfireError mf_desfire_process_status_code(uint8_t status_code);
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance);
#ifdef __cplusplus