diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h index fb50008db..8eb650320 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -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) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index d83a91ad1..bbfb7ffa8 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -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); diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h index 921bbb9de..e55e422fc 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -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( diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 707df42cd..5ac44e26a 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -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. * diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 6d8dfda16..507db2e53 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -1,6 +1,7 @@ #include "mf_desfire_poller_i.h" #include +#include #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, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 5a978d08a..d74b1914b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -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; } diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index 078fbf990..3e09e18a9 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -4,6 +4,7 @@ #include #include +#include #include #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) { diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 03df9a8c8..c1aa5627c 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -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*"