diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h index 8eb650320..3cad11473 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -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) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h index 8dc0513d8..683b12359 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -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. * 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 57210b9c5..21d88623f 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -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, 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 ddb309bad..e9555fcf8 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 @@ -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) { diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f39f80c26..17bb37266 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -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*"