mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
NFC: Support creating CC and NDEF files on DESFire
This commit is contained in:
@@ -17,7 +17,12 @@ extern "C" {
|
||||
#define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F)
|
||||
#define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5)
|
||||
|
||||
#define MF_DESFIRE_CMD_CREATE_APPLICATION (0xCA)
|
||||
#define MF_DESFIRE_CMD_CREATE_APPLICATION (0xCA)
|
||||
#define MF_DESFIRE_CMD_CREATE_STD_DATA_FILE (0xCD)
|
||||
#define MF_DESFIRE_CMD_CREATE_BACKUP_DATA_FILE (0xCB)
|
||||
#define MF_DESFIRE_CMD_CREATE_VALUE_FILE (0xCC)
|
||||
#define MF_DESFIRE_CMD_CREATE_LINEAR_RECORD_FILE (0xC1)
|
||||
#define MF_DESFIRE_CMD_CREATE_CYCLIC_RECORD_FILE (0xC0)
|
||||
|
||||
#define MF_DESFIRE_CMD_READ_DATA (0xBD)
|
||||
#define MF_DESFIRE_CMD_GET_VALUE (0x6C)
|
||||
|
||||
@@ -220,9 +220,9 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] id pointer to the application id for the new application.
|
||||
* @param[in] key_settings pointer to the key settings for the new application.
|
||||
* @param[in] iso_df_id optional identifier for the new application.
|
||||
* @param[in] iso_df_name optional name for the new application.
|
||||
* @param[in] iso_df_name_len length of the optional application name.
|
||||
* @param[in] iso_df_id optional iso identifier for the new application.
|
||||
* @param[in] iso_df_name optional iso name for the new application.
|
||||
* @param[in] iso_df_name_len length of the optional iso application name.
|
||||
* @return MfDesfireErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfDesfireError mf_desfire_poller_create_application(
|
||||
@@ -233,6 +233,23 @@ MfDesfireError mf_desfire_poller_create_application(
|
||||
const uint8_t* iso_df_name,
|
||||
uint8_t iso_df_name_len);
|
||||
|
||||
/**
|
||||
* @brief Create File on MfDesfire card.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] id file id for the new file.
|
||||
* @param[in] data pointer to the file settings for the new file.
|
||||
* @param[in] iso_ef_id optional iso identifier for the new file.
|
||||
* @return MfDesfireErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfDesfireError mf_desfire_poller_create_file(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
const MfDesfireFileSettings* data,
|
||||
uint16_t iso_ef_id);
|
||||
|
||||
/**
|
||||
* @brief Read file data on MfDesfire card.
|
||||
*
|
||||
|
||||
@@ -405,6 +405,7 @@ MfDesfireError mf_desfire_poller_create_application(
|
||||
const uint8_t* iso_df_name,
|
||||
uint8_t iso_df_name_len) {
|
||||
furi_check(instance);
|
||||
furi_check(key_settings);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_CREATE_APPLICATION);
|
||||
@@ -418,9 +419,9 @@ MfDesfireError mf_desfire_poller_create_application(
|
||||
ks2 |= (1 << 5); // Mark file id present
|
||||
bit_buffer_set_byte(instance->input_buffer, ks2_pos, ks2);
|
||||
|
||||
uint8_t iso_file_id_le[sizeof(iso_df_id)];
|
||||
bit_lib_num_to_bytes_le(iso_df_id, sizeof(iso_file_id_le), iso_file_id_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, iso_file_id_le, sizeof(iso_file_id_le));
|
||||
uint8_t iso_df_id_le[sizeof(iso_df_id)];
|
||||
bit_lib_num_to_bytes_le(iso_df_id, sizeof(iso_df_id_le), iso_df_id_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, iso_df_id_le, sizeof(iso_df_id_le));
|
||||
|
||||
bit_buffer_append_bytes(instance->input_buffer, iso_df_name, iso_df_name_len);
|
||||
}
|
||||
@@ -431,6 +432,73 @@ MfDesfireError mf_desfire_poller_create_application(
|
||||
return error;
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_create_file(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
const MfDesfireFileSettings* data,
|
||||
uint16_t iso_ef_id) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(
|
||||
instance->input_buffer,
|
||||
data->type == MfDesfireFileTypeStandard ? MF_DESFIRE_CMD_CREATE_STD_DATA_FILE :
|
||||
data->type == MfDesfireFileTypeBackup ? MF_DESFIRE_CMD_CREATE_BACKUP_DATA_FILE :
|
||||
data->type == MfDesfireFileTypeValue ? MF_DESFIRE_CMD_CREATE_VALUE_FILE :
|
||||
data->type == MfDesfireFileTypeLinearRecord ? MF_DESFIRE_CMD_CREATE_LINEAR_RECORD_FILE :
|
||||
data->type == MfDesfireFileTypeCyclicRecord ? MF_DESFIRE_CMD_CREATE_CYCLIC_RECORD_FILE :
|
||||
0x00);
|
||||
bit_buffer_append_byte(instance->input_buffer, id);
|
||||
if(iso_ef_id) {
|
||||
uint8_t iso_ef_id_le[sizeof(iso_ef_id)];
|
||||
bit_lib_num_to_bytes_le(iso_ef_id, sizeof(iso_ef_id_le), iso_ef_id_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, iso_ef_id_le, sizeof(iso_ef_id_le));
|
||||
}
|
||||
bit_buffer_append_byte(instance->input_buffer, data->comm);
|
||||
bit_buffer_append_bytes(
|
||||
instance->input_buffer,
|
||||
(const uint8_t*)data->access_rights,
|
||||
sizeof(MfDesfireFileAccessRights) * data->access_rights_len);
|
||||
|
||||
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
|
||||
uint8_t data_size_le[3 * sizeof(uint8_t)];
|
||||
bit_lib_num_to_bytes_le(data->data.size, sizeof(data_size_le), data_size_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, data_size_le, sizeof(data_size_le));
|
||||
|
||||
} else if(data->type == MfDesfireFileTypeValue) {
|
||||
uint8_t lo_limit_le[sizeof(data->value.lo_limit)];
|
||||
bit_lib_num_to_bytes_le(data->value.lo_limit, sizeof(lo_limit_le), lo_limit_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, lo_limit_le, sizeof(lo_limit_le));
|
||||
|
||||
uint8_t hi_limit_le[sizeof(data->value.hi_limit)];
|
||||
bit_lib_num_to_bytes_le(data->value.hi_limit, sizeof(hi_limit_le), hi_limit_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, hi_limit_le, sizeof(hi_limit_le));
|
||||
|
||||
uint8_t value_le[sizeof(data->value.limited_credit_value)];
|
||||
bit_lib_num_to_bytes_le(data->value.limited_credit_value, sizeof(value_le), value_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, value_le, sizeof(value_le));
|
||||
|
||||
bit_buffer_append_byte(instance->input_buffer, data->value.limited_credit_enabled);
|
||||
|
||||
} else if(
|
||||
data->type == MfDesfireFileTypeLinearRecord ||
|
||||
data->type == MfDesfireFileTypeCyclicRecord) {
|
||||
uint8_t record_size_le[3 * sizeof(uint8_t)];
|
||||
bit_lib_num_to_bytes_le(data->record.size, sizeof(record_size_le), record_size_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, record_size_le, sizeof(record_size_le));
|
||||
|
||||
uint8_t record_max_le[3 * sizeof(uint8_t)];
|
||||
bit_lib_num_to_bytes_le(data->record.max, sizeof(record_max_le), record_max_le);
|
||||
bit_buffer_append_bytes(instance->input_buffer, record_max_le, sizeof(record_max_le));
|
||||
}
|
||||
|
||||
MfDesfireError error =
|
||||
mf_desfire_poller_send_chunks(instance, instance->input_buffer, instance->result_buffer);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static MfDesfireError mf_desfire_poller_read_file(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
|
||||
@@ -10,7 +10,31 @@
|
||||
#define TAG "Type4TagPoller"
|
||||
|
||||
static const MfDesfireApplicationId mf_des_picc_app_id = {.data = {0x00, 0x00, 0x00}};
|
||||
static const MfDesfireApplicationId mf_des_ndef_app_id = {.data = {0x10, 0xEE, 0xEE}};
|
||||
static const MfDesfireApplicationId mf_des_t4t_app_id = {.data = {0x10, 0xEE, 0xEE}};
|
||||
static const MfDesfireKeySettings mf_des_t4t_app_key_settings = {
|
||||
.is_master_key_changeable = true,
|
||||
.is_free_directory_list = true,
|
||||
.is_free_create_delete = true,
|
||||
.is_config_changeable = true,
|
||||
.change_key_id = 0,
|
||||
.max_keys = 1,
|
||||
.flags = 0,
|
||||
};
|
||||
#define MF_DES_T4T_CC_FILE_ID (0x01)
|
||||
static const MfDesfireFileSettings mf_des_t4t_cc_file = {
|
||||
.type = MfDesfireFileTypeStandard,
|
||||
.comm = MfDesfireFileCommunicationSettingsPlaintext,
|
||||
.access_rights[0] = 0xEEEE,
|
||||
.access_rights_len = 1,
|
||||
.data.size = TYPE_4_TAG_T4T_CC_MIN_SIZE,
|
||||
};
|
||||
#define MF_DES_T4T_NDEF_FILE_ID (0x02)
|
||||
static const MfDesfireFileSettings mf_des_t4t_ndef_file_default = {
|
||||
.type = MfDesfireFileTypeStandard,
|
||||
.comm = MfDesfireFileCommunicationSettingsPlaintext,
|
||||
.access_rights[0] = 0xEEE0,
|
||||
.access_rights_len = 1,
|
||||
};
|
||||
|
||||
Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) {
|
||||
furi_check(instance);
|
||||
@@ -319,27 +343,18 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) {
|
||||
MfDesfireError mf_des_error;
|
||||
|
||||
do {
|
||||
// Select PICC (Card) level
|
||||
FURI_LOG_D(TAG, "Select DESFire PICC");
|
||||
mf_des_error = mf_desfire_poller_select_application(mf_des, &mf_des_picc_app_id);
|
||||
if(mf_des_error != MfDesfireErrorNone) {
|
||||
error = Type4TagErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create NDEF application
|
||||
MfDesfireKeySettings key_settings = {
|
||||
.is_master_key_changeable = true,
|
||||
.is_free_directory_list = true,
|
||||
.is_free_create_delete = true,
|
||||
.is_config_changeable = true,
|
||||
.change_key_id = 0,
|
||||
.max_keys = 1,
|
||||
.flags = 0,
|
||||
};
|
||||
FURI_LOG_D(TAG, "Create DESFire T4T app");
|
||||
mf_des_error = mf_desfire_poller_create_application(
|
||||
mf_des,
|
||||
&mf_des_ndef_app_id,
|
||||
&key_settings,
|
||||
&mf_des_t4t_app_id,
|
||||
&mf_des_t4t_app_key_settings,
|
||||
TYPE_4_TAG_ISO_DF_ID,
|
||||
type_4_tag_iso_df_name,
|
||||
sizeof(type_4_tag_iso_df_name));
|
||||
@@ -363,13 +378,87 @@ Type4TagError type_4_tag_poller_create_app(Type4TagPoller* instance) {
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_poller_create_cc(Type4TagPoller* instance) {
|
||||
UNUSED(instance);
|
||||
return Type4TagErrorNotSupported;
|
||||
Type4TagError error = Type4TagErrorNotSupported;
|
||||
|
||||
if(instance->data->platform == Type4TagPlatformMfDesfire) {
|
||||
MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller);
|
||||
mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped);
|
||||
MfDesfireError mf_des_error;
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Create DESFire CC");
|
||||
mf_des_error = mf_desfire_poller_create_file(
|
||||
mf_des, MF_DES_T4T_CC_FILE_ID, &mf_des_t4t_cc_file, TYPE_4_TAG_T4T_CC_EF_ID);
|
||||
if(mf_des_error != MfDesfireErrorNone) {
|
||||
if(mf_des_error != MfDesfireErrorNotPresent &&
|
||||
mf_des_error != MfDesfireErrorTimeout) {
|
||||
error = Type4TagErrorCardLocked;
|
||||
} else {
|
||||
error = Type4TagErrorProtocol;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Select CC");
|
||||
error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_EF_ID);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
FURI_LOG_D(TAG, "Write DESFire CC");
|
||||
instance->data->t4t_version.value = TYPE_4_TAG_T4T_CC_VNO;
|
||||
instance->data->chunk_max_read = 0x3A;
|
||||
instance->data->chunk_max_write = 0x34;
|
||||
instance->data->ndef_file_id = TYPE_4_TAG_T4T_NDEF_EF_ID;
|
||||
instance->data->ndef_max_len = TYPE_4_TAG_DEFAULT_NDEF_SIZE;
|
||||
instance->data->ndef_read_lock = TYPE_4_TAG_T4T_CC_RW_LOCK_NONE;
|
||||
instance->data->ndef_write_lock = TYPE_4_TAG_T4T_CC_RW_LOCK_NONE;
|
||||
instance->data->is_tag_specific = true;
|
||||
uint8_t cc_buf[TYPE_4_TAG_T4T_CC_MIN_SIZE];
|
||||
type_4_tag_cc_dump(instance->data, cc_buf, sizeof(cc_buf));
|
||||
error = type_5_tag_poller_iso_write(instance, 0, sizeof(cc_buf), cc_buf);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
error = Type4TagErrorNone;
|
||||
} while(false);
|
||||
|
||||
mf_desfire_poller.free(mf_des);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_poller_create_ndef(Type4TagPoller* instance) {
|
||||
UNUSED(instance);
|
||||
return Type4TagErrorNotSupported;
|
||||
Type4TagError error = Type4TagErrorNotSupported;
|
||||
|
||||
if(instance->data->platform == Type4TagPlatformMfDesfire) {
|
||||
MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller);
|
||||
mf_desfire_poller_set_command_mode(mf_des, MfDesfirePollerCommandModeIsoWrapped);
|
||||
MfDesfireError mf_des_error;
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Create DESFire NDEF");
|
||||
MfDesfireFileSettings mf_des_t4t_ndef_file = mf_des_t4t_ndef_file_default;
|
||||
mf_des_t4t_ndef_file.data.size = sizeof(uint16_t) + (instance->data->is_tag_specific ?
|
||||
instance->data->ndef_max_len :
|
||||
TYPE_4_TAG_DEFAULT_NDEF_SIZE);
|
||||
mf_des_error = mf_desfire_poller_create_file(
|
||||
mf_des, MF_DES_T4T_NDEF_FILE_ID, &mf_des_t4t_ndef_file, TYPE_4_TAG_T4T_NDEF_EF_ID);
|
||||
if(mf_des_error != MfDesfireErrorNone) {
|
||||
if(mf_des_error != MfDesfireErrorNotPresent &&
|
||||
mf_des_error != MfDesfireErrorTimeout) {
|
||||
error = Type4TagErrorCardLocked;
|
||||
} else {
|
||||
error = Type4TagErrorProtocol;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
error = Type4TagErrorNone;
|
||||
} while(false);
|
||||
|
||||
mf_desfire_poller.free(mf_des);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_poller_write_ndef(Type4TagPoller* instance) {
|
||||
|
||||
@@ -2637,6 +2637,7 @@ Function,+,mf_desfire_get_uid,const uint8_t*,"const MfDesfireData*, size_t*"
|
||||
Function,+,mf_desfire_is_equal,_Bool,"const MfDesfireData*, const MfDesfireData*"
|
||||
Function,+,mf_desfire_load,_Bool,"MfDesfireData*, FlipperFormat*, uint32_t"
|
||||
Function,+,mf_desfire_poller_create_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*, const MfDesfireKeySettings*, uint16_t, const uint8_t*, uint8_t"
|
||||
Function,+,mf_desfire_poller_create_file,MfDesfireError,"MfDesfirePoller*, MfDesfireFileId, const MfDesfireFileSettings*, uint16_t"
|
||||
Function,+,mf_desfire_poller_read_application,MfDesfireError,"MfDesfirePoller*, MfDesfireApplication*"
|
||||
Function,+,mf_desfire_poller_read_application_ids,MfDesfireError,"MfDesfirePoller*, SimpleArray*"
|
||||
Function,+,mf_desfire_poller_read_applications,MfDesfireError,"MfDesfirePoller*, const SimpleArray*, SimpleArray*"
|
||||
|
||||
|
Reference in New Issue
Block a user