[FL-3675] Ntag21x write (#3246)

* New scenes for ultralight poller write mode
* Added new button and transition logic for write operation
   For now write is only possible for NTAG21x cards with default password and no AUTHLIM set
* Poller states extended
* Enums and datatypes extended for new poller mode
* Added mode field to poller instance datatype
* New states for poller added in order to implement write mode
* Added new event type for locked cards in order to simplify state flow
* New logic for poller write commands
* Scenes adjustments
* Scenes renamed
* New field added to poller instance
* Now we write in 'page per call' mode
* Now function takes callback return value into account
* Callback will be called only in write mode
* Event type added
* Log adjusted and start page to write set
* Logs added and check in now false at start, then it moves to true
* Now mf_ultralight_poller_handler_request_write_data halts card in case of check failure and stops poller
* All fail events now returns NfcCommandStop callback
* In case of fail we move back properly
* Remove garbage

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
RebornedBrain
2023-12-02 07:45:47 +03:00
committed by GitHub
parent b51a754fd9
commit 6a5d63803a
9 changed files with 462 additions and 4 deletions

View File

@@ -224,11 +224,24 @@ static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance
instance->tearing_flag_read = 0;
instance->tearing_flag_total = 3;
instance->pages_read = 0;
instance->state = MfUltralightPollerStateReadVersion;
instance->state = MfUltralightPollerStateRequestMode;
instance->current_page = 0;
return NfcCommandContinue;
}
static NfcCommand mf_ultralight_poller_handler_request_mode(MfUltralightPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->mfu_event.type = MfUltralightPollerEventTypeRequestMode;
instance->mfu_event.data->poller_mode = MfUltralightPollerModeRead;
command = instance->callback(instance->general_event, instance->context);
instance->mode = instance->mfu_event.data->poller_mode;
instance->state = MfUltralightPollerStateReadVersion;
return command;
}
static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) {
instance->error = mf_ultralight_poller_read_version(instance, &instance->data->version);
if(instance->error == MfUltralightErrorNone) {
@@ -259,6 +272,7 @@ static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPo
}
static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller* instance) {
MfUltralightPollerState next_state = MfUltralightPollerStateGetFeatureSet;
MfUltralightPageReadCommandData data = {};
instance->error = mf_ultralight_poller_read_page(instance, 41, &data);
if(instance->error == MfUltralightErrorNone) {
@@ -268,8 +282,13 @@ static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller
FURI_LOG_D(TAG, "Original Ultralight detected");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->data->type = MfUltralightTypeUnknown;
if(instance->mode == MfUltralightPollerModeWrite) {
instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch;
instance->callback(instance->general_event, instance->context);
next_state = MfUltralightPollerStateWriteFail;
}
}
instance->state = MfUltralightPollerStateGetFeatureSet;
instance->state = next_state;
return NfcCommandContinue;
}
@@ -508,6 +527,7 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll
static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) {
FURI_LOG_D(TAG, "Read Failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed;
instance->mfu_event.data->error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->state = MfUltralightPollerStateIdle;
@@ -516,15 +536,121 @@ static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* ins
static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) {
FURI_LOG_D(TAG, "Read success");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess;
NfcCommand command = instance->callback(instance->general_event, instance->context);
if(instance->mode == MfUltralightPollerModeRead) {
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->state = MfUltralightPollerStateIdle;
} else {
instance->state = MfUltralightPollerStateRequestWriteData;
}
return command;
}
static NfcCommand mf_ultralight_poller_handler_request_write_data(MfUltralightPoller* instance) {
FURI_LOG_D(TAG, "Check writing capability");
NfcCommand command = NfcCommandContinue;
MfUltralightPollerState next_state = MfUltralightPollerStateWritePages;
instance->current_page = 4;
instance->mfu_event.type = MfUltralightPollerEventTypeRequestWriteData;
instance->callback(instance->general_event, instance->context);
const MfUltralightData* write_data = instance->mfu_event.data->write_data;
const MfUltralightData* tag_data = instance->data;
uint32_t features = mf_ultralight_get_feature_support_set(tag_data->type);
bool check_passed = false;
do {
if(write_data->type != tag_data->type) {
FURI_LOG_D(TAG, "Incorrect tag type");
instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch;
break;
}
if(!instance->auth_context.auth_success) {
FURI_LOG_D(TAG, "Unknown password");
instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;
break;
}
const MfUltralightPage staticlock_page = tag_data->page[2];
if(staticlock_page.data[2] != 0 || staticlock_page.data[3] != 0) {
FURI_LOG_D(TAG, "Static lock bits are set");
instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;
break;
}
if(mf_ultralight_support_feature(features, MfUltralightFeatureSupportDynamicLock)) {
uint8_t dynlock_num = mf_ultralight_get_config_page_num(tag_data->type) - 1;
const MfUltralightPage dynlock_page = tag_data->page[dynlock_num];
if(dynlock_page.data[0] != 0 || dynlock_page.data[1] != 0) {
FURI_LOG_D(TAG, "Dynamic lock bits are set");
instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;
break;
}
}
check_passed = true;
} while(false);
if(!check_passed) {
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
command = instance->callback(instance->general_event, instance->context);
next_state = MfUltralightPollerStateWriteFail;
}
instance->state = next_state;
return command;
}
static NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* instance) {
NfcCommand command = NfcCommandContinue;
do {
const MfUltralightData* write_data = instance->mfu_event.data->write_data;
uint8_t end_page = mf_ultralight_get_config_page_num(write_data->type) - 1;
if(instance->current_page == end_page) {
instance->state = MfUltralightPollerStateWriteSuccess;
break;
}
FURI_LOG_D(TAG, "Writing page %d", instance->current_page);
MfUltralightError error = mf_ultralight_poller_write_page(
instance, instance->current_page, &write_data->page[instance->current_page]);
if(error != MfUltralightErrorNone) {
instance->state = MfUltralightPollerStateWriteFail;
instance->error = error;
break;
}
instance->current_page++;
} while(false);
return command;
}
static NfcCommand mf_ultralight_poller_handler_write_fail(MfUltralightPoller* instance) {
FURI_LOG_D(TAG, "Write failed");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->mfu_event.data->error = instance->error;
instance->mfu_event.type = MfUltralightPollerEventTypeWriteFail;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static NfcCommand mf_ultralight_poller_handler_write_success(MfUltralightPoller* instance) {
FURI_LOG_D(TAG, "Write success");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->mfu_event.type = MfUltralightPollerEventTypeWriteSuccess;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const MfUltralightPollerReadHandler
mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = {
[MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle,
[MfUltralightPollerStateRequestMode] = mf_ultralight_poller_handler_request_mode,
[MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version,
[MfUltralightPollerStateDetectMfulC] = mf_ultralight_poller_handler_check_ultralight_c,
[MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203,
@@ -538,6 +664,11 @@ static const MfUltralightPollerReadHandler
[MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages,
[MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail,
[MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success,
[MfUltralightPollerStateRequestWriteData] =
mf_ultralight_poller_handler_request_write_data,
[MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages,
[MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail,
[MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success,
};

View File

@@ -16,13 +16,27 @@ typedef struct MfUltralightPoller MfUltralightPoller;
* @brief Enumeration of possible MfUltralight poller event types.
*/
typedef enum {
MfUltralightPollerEventTypeRequestMode, /**< Poller requests for operating mode. */
MfUltralightPollerEventTypeAuthRequest, /**< Poller requests to fill authentication context. */
MfUltralightPollerEventTypeAuthSuccess, /**< Authentication succeeded. */
MfUltralightPollerEventTypeAuthFailed, /**< Authentication failed. */
MfUltralightPollerEventTypeReadSuccess, /**< Poller read card successfully. */
MfUltralightPollerEventTypeReadFailed, /**< Poller failed to read card. */
MfUltralightPollerEventTypeRequestWriteData, /**< Poller request card data for write operation. */
MfUltralightPollerEventTypeCardMismatch, /**< Type of card for writing differs from presented one. */
MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */
MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */
MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */
} MfUltralightPollerEventType;
/**
* @brief Enumeration of possible MfUltralight poller operating modes.
*/
typedef enum {
MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */
MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */
} MfUltralightPollerMode;
/**
* @brief MfUltralight poller authentication context.
*/
@@ -39,6 +53,8 @@ typedef struct {
typedef union {
MfUltralightPollerAuthContext auth_context; /**< Authentication context. */
MfUltralightError error; /**< Error code indicating reading fail reason. */
const MfUltralightData* write_data;
MfUltralightPollerMode poller_mode;
} MfUltralightPollerEventData;
/**

View File

@@ -49,6 +49,7 @@ typedef union {
typedef enum {
MfUltralightPollerStateIdle,
MfUltralightPollerStateRequestMode,
MfUltralightPollerStateReadVersion,
MfUltralightPollerStateDetectMfulC,
MfUltralightPollerStateDetectNtag203,
@@ -61,6 +62,10 @@ typedef enum {
MfUltralightPollerStateTryDefaultPass,
MfUltralightPollerStateReadFailed,
MfUltralightPollerStateReadSuccess,
MfUltralightPollerStateRequestWriteData,
MfUltralightPollerStateWritePages,
MfUltralightPollerStateWriteFail,
MfUltralightPollerStateWriteSuccess,
MfUltralightPollerStateNum,
} MfUltralightPollerState;
@@ -68,6 +73,7 @@ typedef enum {
struct MfUltralightPoller {
Iso14443_3aPoller* iso14443_3a_poller;
MfUltralightPollerState state;
MfUltralightPollerMode mode;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
MfUltralightData* data;
@@ -79,6 +85,7 @@ struct MfUltralightPoller {
uint8_t counters_total;
uint8_t tearing_flag_read;
uint8_t tearing_flag_total;
uint16_t current_page;
MfUltralightError error;
NfcGenericEvent general_event;