NFC: Support creating NDEF AID on DESFire

This commit is contained in:
Willy-JL
2025-03-19 06:37:13 +00:00
parent 27c977b7c2
commit f7198c6105
8 changed files with 160 additions and 16 deletions

View File

@@ -17,6 +17,8 @@ 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_READ_DATA (0xBD)
#define MF_DESFIRE_CMD_GET_VALUE (0x6C)
#define MF_DESFIRE_CMD_READ_RECORDS (0xBB)

View File

@@ -75,17 +75,17 @@ bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* bu
return can_parse;
}
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) {
typedef struct FURI_PACKED {
bool is_master_key_changeable : 1;
bool is_free_directory_list : 1;
bool is_free_create_delete : 1;
bool is_config_changeable : 1;
uint8_t change_key_id : 4;
uint8_t max_keys : 4;
uint8_t flags : 4;
} MfDesfireKeySettingsLayout;
typedef struct FURI_PACKED {
bool is_master_key_changeable : 1;
bool is_free_directory_list : 1;
bool is_free_create_delete : 1;
bool is_config_changeable : 1;
uint8_t change_key_id : 4;
uint8_t max_keys : 4;
uint8_t flags : 4;
} MfDesfireKeySettingsLayout;
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout);
if(can_parse) {
@@ -105,6 +105,21 @@ bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer*
return can_parse;
}
void mf_desfire_key_settings_dump(const MfDesfireKeySettings* data, BitBuffer* buf) {
MfDesfireKeySettingsLayout layout;
layout.is_master_key_changeable = data->is_master_key_changeable;
layout.is_free_directory_list = data->is_free_directory_list;
layout.is_free_create_delete = data->is_free_create_delete;
layout.is_config_changeable = data->is_config_changeable;
layout.change_key_id = data->change_key_id;
layout.max_keys = data->max_keys;
layout.flags = data->flags;
bit_buffer_append_bytes(buf, (uint8_t*)&layout, sizeof(MfDesfireKeySettingsLayout));
}
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion);

View File

@@ -68,6 +68,8 @@ bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* bu
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf);
void mf_desfire_key_settings_dump(const MfDesfireKeySettings* data, BitBuffer* buf);
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf);
bool mf_desfire_application_id_parse(

View File

@@ -187,6 +187,27 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
const SimpleArray* file_ids,
SimpleArray* data);
/**
* @brief Create Application 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 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.
* @return MfDesfireErrorNone on success, an error code on failure.
*/
MfDesfireError mf_desfire_poller_create_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id,
const MfDesfireKeySettings* key_settings,
uint16_t iso_df_id,
const uint8_t* iso_df_name,
uint8_t iso_df_name_len);
/**
* @brief Read file data on MfDesfire card.
*

View File

@@ -1,6 +1,7 @@
#include "mf_desfire_poller_i.h"
#include <furi.h>
#include <bit_lib/bit_lib.h>
#include "mf_desfire_i.h"
@@ -329,6 +330,40 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
return error;
}
MfDesfireError mf_desfire_poller_create_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id,
const MfDesfireKeySettings* key_settings,
uint16_t iso_df_id,
const uint8_t* iso_df_name,
uint8_t iso_df_name_len) {
furi_check(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_CREATE_APPLICATION);
bit_buffer_append_bytes(
instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));
mf_desfire_key_settings_dump(key_settings, instance->input_buffer);
if(iso_df_name && iso_df_name_len) {
uint8_t ks2_pos = bit_buffer_get_size_bytes(instance->input_buffer) - 1;
uint8_t ks2 = bit_buffer_get_byte(instance->input_buffer, ks2_pos);
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));
bit_buffer_append_bytes(instance->input_buffer, iso_df_name, iso_df_name_len);
}
MfDesfireError error =
mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
return error;
}
static MfDesfireError mf_desfire_poller_read_file(
MfDesfirePoller* instance,
MfDesfireFileId id,

View File

@@ -68,7 +68,6 @@ static NfcCommand type_4_tag_poller_handler_request_mode(Type4TagPoller* instanc
static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* instance) {
instance->error = type_4_tag_poller_detect_platform(instance);
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
if(instance->error == Type4TagErrorNone) {
FURI_LOG_D(TAG, "Detect platform success");
} else {
@@ -76,6 +75,8 @@ static NfcCommand type_4_tag_poller_handler_detect_platform(Type4TagPoller* inst
}
instance->state = Type4TagPollerStateSelectApplication;
// Reset card state so platform-specific commands do not interfere
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
}
@@ -89,6 +90,9 @@ static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance)
if(instance->mode == Type4TagPollerModeWrite &&
instance->error == Type4TagErrorCardUnformatted) {
instance->state = Type4TagPollerStateCreateApplication;
// Reset card state so platform-specific commands do not interfere
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
} else {
instance->state = Type4TagPollerStateFailed;
}
@@ -109,6 +113,9 @@ static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) {
if(instance->mode == Type4TagPollerModeWrite &&
instance->error == Type4TagErrorCardUnformatted) {
instance->state = Type4TagPollerStateCreateCapabilityContainer;
// Reset card state so platform-specific commands do not interfere
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
} else {
instance->state = Type4TagPollerStateFailed;
}
@@ -134,7 +141,10 @@ static NfcCommand type_4_tag_poller_handler_create_app(Type4TagPoller* instance)
instance->error = type_4_tag_poller_create_app(instance);
if(instance->error == Type4TagErrorNone) {
FURI_LOG_D(TAG, "Create application success");
// Reset card state so platform-specific commands do not interfere
instance->state = Type4TagPollerStateSelectApplication;
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
} else {
FURI_LOG_E(TAG, "Failed to create application");
instance->state = Type4TagPollerStateFailed;
@@ -147,7 +157,10 @@ static NfcCommand type_4_tag_poller_handler_create_cc(Type4TagPoller* instance)
instance->error = type_4_tag_poller_create_cc(instance);
if(instance->error == Type4TagErrorNone) {
FURI_LOG_D(TAG, "Create CC success");
instance->state = Type4TagPollerStateReadCapabilityContainer;
// Reset card state so platform-specific commands do not interfere
instance->state = Type4TagPollerStateSelectApplication;
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
} else {
FURI_LOG_E(TAG, "Failed to create CC");
instance->state = Type4TagPollerStateFailed;
@@ -160,7 +173,10 @@ static NfcCommand type_4_tag_poller_handler_create_ndef(Type4TagPoller* instance
instance->error = type_4_tag_poller_create_ndef(instance);
if(instance->error == Type4TagErrorNone) {
FURI_LOG_D(TAG, "Create NDEF success");
instance->state = Type4TagPollerStateWriteNdefMessage;
// Reset card state so platform-specific commands do not interfere
instance->state = Type4TagPollerStateSelectApplication;
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
} else {
FURI_LOG_E(TAG, "Failed to create NDEF");
instance->state = Type4TagPollerStateFailed;
@@ -179,6 +195,9 @@ static NfcCommand type_4_tag_poller_handler_write_ndef(Type4TagPoller* instance)
if(instance->mode == Type4TagPollerModeWrite &&
instance->error == Type4TagErrorCardUnformatted) {
instance->state = Type4TagPollerStateCreateNdefMessage;
// Reset card state so platform-specific commands do not interfere
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
return NfcCommandReset;
} else {
instance->state = Type4TagPollerStateFailed;
}

View File

@@ -4,6 +4,7 @@
#include <bit_lib/bit_lib.h>
#include <nfc/nfc_device.h>
#include <nfc/protocols/mf_desfire/mf_desfire_poller.h>
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
#define TAG "Type4TagPoller"
@@ -199,7 +200,7 @@ Type4TagError type_4_tag_poller_detect_platform(Type4TagPoller* instance) {
do {
FURI_LOG_D(TAG, "Detect DESFire");
NfcGenericInstance* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller);
MfDesfirePoller* mf_des = mf_desfire_poller.alloc(instance->iso14443_4a_poller);
if(mf_desfire_poller.detect(event, mf_des)) {
platform = Type4TagPlatformMfDesfire;
nfc_device_set_data(device, NfcProtocolMfDesfire, mf_desfire_poller.get_data(mf_des));
@@ -303,8 +304,56 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) {
}
Type4TagError type_4_tag_poller_create_app(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);
MfDesfireError mf_des_error;
do {
// Select PICC (Card) level
MfDesfireApplicationId picc_aid = {{0x00, 0x00, 0x00}};
mf_des_error = mf_desfire_poller_select_application(mf_des, &picc_aid);
if(mf_des_error != MfDesfireErrorNone) {
error = Type4TagErrorProtocol;
break;
}
// Create NDEF application
MfDesfireApplicationId ndef_aid = {{0x10, 0xEE, 0xEE}};
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,
};
mf_des_error = mf_desfire_poller_create_application(
mf_des,
&ndef_aid,
&key_settings,
TYPE_4_TAG_ISO_DF_ID,
type_4_tag_iso_df_name,
sizeof(type_4_tag_iso_df_name));
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_create_cc(Type4TagPoller* instance) {

View File

@@ -2636,6 +2636,7 @@ Function,+,mf_desfire_get_file_settings,const MfDesfireFileSettings*,"const MfDe
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_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*"
1 entry status name type params
2636 Function + mf_desfire_get_uid const uint8_t* const MfDesfireData*, size_t*
2637 Function + mf_desfire_is_equal _Bool const MfDesfireData*, const MfDesfireData*
2638 Function + mf_desfire_load _Bool MfDesfireData*, FlipperFormat*, uint32_t
2639 Function + mf_desfire_poller_create_application MfDesfireError MfDesfirePoller*, const MfDesfireApplicationId*, const MfDesfireKeySettings*, uint16_t, const uint8_t*, uint8_t
2640 Function + mf_desfire_poller_read_application MfDesfireError MfDesfirePoller*, MfDesfireApplication*
2641 Function + mf_desfire_poller_read_application_ids MfDesfireError MfDesfirePoller*, SimpleArray*
2642 Function + mf_desfire_poller_read_applications MfDesfireError MfDesfirePoller*, const SimpleArray*, SimpleArray*