mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-27 07:11:45 +03:00
dd988ba449
* nfc_util functions for processing bytes moved into bit_lib * bitlib test update * bit_lib moved from lfrfid to standalone lib * Added bit functions for any supported data types * Error fix and api add * Added test for 64 * Added doc * Testcase for 64 rewrited * Realization error fix * API version bump * sync api version, fix after-merge old libs usage * fix build errors * build fix * fbt format Co-authored-by: assasinfil <nfa57643@gmail.com> Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
729 lines
28 KiB
C
729 lines
28 KiB
C
#include "mf_ultralight_poller_i.h"
|
|
|
|
#include <nfc/protocols/nfc_poller_base.h>
|
|
|
|
#include <furi.h>
|
|
|
|
#define TAG "MfUltralightPoller"
|
|
|
|
typedef NfcCommand (*MfUltralightPollerReadHandler)(MfUltralightPoller* instance);
|
|
|
|
static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k(
|
|
uint16_t lin_addr,
|
|
uint8_t* sector,
|
|
uint8_t* tag,
|
|
uint8_t* pages_left) {
|
|
bool tag_calculated = false;
|
|
// 0 - 226: sector 0
|
|
// 227 - 228: config registers
|
|
// 229 - 230: session registers
|
|
|
|
if(lin_addr > 230) {
|
|
*pages_left = 0;
|
|
} else if(lin_addr >= 229) {
|
|
*sector = 3;
|
|
*pages_left = 2 - (lin_addr - 229);
|
|
*tag = lin_addr - 229 + 248;
|
|
tag_calculated = true;
|
|
} else if(lin_addr >= 227) {
|
|
*sector = 0;
|
|
*pages_left = 2 - (lin_addr - 227);
|
|
*tag = lin_addr - 227 + 232;
|
|
tag_calculated = true;
|
|
} else {
|
|
*sector = 0;
|
|
*pages_left = 227 - lin_addr;
|
|
*tag = lin_addr;
|
|
tag_calculated = true;
|
|
}
|
|
|
|
return tag_calculated;
|
|
}
|
|
|
|
static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k(
|
|
uint16_t lin_addr,
|
|
uint8_t* sector,
|
|
uint8_t* tag,
|
|
uint8_t* pages_left) {
|
|
bool tag_calculated = false;
|
|
// 0 - 255: sector 0
|
|
// 256 - 480: sector 1
|
|
// 481 - 482: config registers
|
|
// 483 - 484: session registers
|
|
|
|
if(lin_addr > 484) {
|
|
*pages_left = 0;
|
|
} else if(lin_addr >= 483) {
|
|
*sector = 3;
|
|
*pages_left = 2 - (lin_addr - 483);
|
|
*tag = lin_addr - 483 + 248;
|
|
tag_calculated = true;
|
|
} else if(lin_addr >= 481) {
|
|
*sector = 1;
|
|
*pages_left = 2 - (lin_addr - 481);
|
|
*tag = lin_addr - 481 + 232;
|
|
tag_calculated = true;
|
|
} else if(lin_addr >= 256) {
|
|
*sector = 1;
|
|
*pages_left = 225 - (lin_addr - 256);
|
|
*tag = lin_addr - 256;
|
|
tag_calculated = true;
|
|
} else {
|
|
*sector = 0;
|
|
*pages_left = 256 - lin_addr;
|
|
*tag = lin_addr;
|
|
tag_calculated = true;
|
|
}
|
|
|
|
return tag_calculated;
|
|
}
|
|
|
|
static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k(
|
|
uint16_t lin_addr,
|
|
uint8_t* sector,
|
|
uint8_t* tag,
|
|
uint8_t* pages_left) {
|
|
bool tag_calculated = false;
|
|
// 0 - 233: sector 0 + registers
|
|
// 234 - 235: session registers
|
|
|
|
if(lin_addr > 235) {
|
|
*pages_left = 0;
|
|
} else if(lin_addr >= 234) {
|
|
*sector = 0;
|
|
*pages_left = 2 - (lin_addr - 234);
|
|
*tag = lin_addr - 234 + 236;
|
|
tag_calculated = true;
|
|
} else {
|
|
*sector = 0;
|
|
*pages_left = 234 - lin_addr;
|
|
*tag = lin_addr;
|
|
tag_calculated = true;
|
|
}
|
|
|
|
return tag_calculated;
|
|
}
|
|
|
|
static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k(
|
|
uint16_t lin_addr,
|
|
uint8_t* sector,
|
|
uint8_t* tag,
|
|
uint8_t* pages_left) {
|
|
bool tag_calculated = false;
|
|
// 0 - 233: sector 0 + registers
|
|
// 234 - 235: session registers
|
|
// 236 - 491: sector 1
|
|
|
|
if(lin_addr > 491) {
|
|
*pages_left = 0;
|
|
} else if(lin_addr >= 236) {
|
|
*sector = 1;
|
|
*pages_left = 256 - (lin_addr - 236);
|
|
*tag = lin_addr - 236;
|
|
tag_calculated = true;
|
|
} else if(lin_addr >= 234) {
|
|
*sector = 0;
|
|
*pages_left = 2 - (lin_addr - 234);
|
|
*tag = lin_addr - 234 + 236;
|
|
tag_calculated = true;
|
|
} else {
|
|
*sector = 0;
|
|
*pages_left = 234 - lin_addr;
|
|
*tag = lin_addr;
|
|
tag_calculated = true;
|
|
}
|
|
|
|
return tag_calculated;
|
|
}
|
|
|
|
bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag(
|
|
MfUltralightPoller* instance,
|
|
uint16_t lin_addr,
|
|
uint8_t* sector,
|
|
uint8_t* tag,
|
|
uint8_t* pages_left) {
|
|
furi_assert(instance);
|
|
furi_assert(sector);
|
|
furi_assert(tag);
|
|
furi_assert(pages_left);
|
|
|
|
bool tag_calculated = false;
|
|
|
|
if(instance->data->type == MfUltralightTypeNTAGI2C1K) {
|
|
tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k(
|
|
lin_addr, sector, tag, pages_left);
|
|
} else if(instance->data->type == MfUltralightTypeNTAGI2C2K) {
|
|
tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k(
|
|
lin_addr, sector, tag, pages_left);
|
|
} else if(instance->data->type == MfUltralightTypeNTAGI2CPlus1K) {
|
|
tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k(
|
|
lin_addr, sector, tag, pages_left);
|
|
} else if(instance->data->type == MfUltralightTypeNTAGI2CPlus2K) {
|
|
tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k(
|
|
lin_addr, sector, tag, pages_left);
|
|
}
|
|
|
|
return tag_calculated;
|
|
}
|
|
|
|
MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {
|
|
furi_assert(iso14443_3a_poller);
|
|
|
|
MfUltralightPoller* instance = malloc(sizeof(MfUltralightPoller));
|
|
instance->iso14443_3a_poller = iso14443_3a_poller;
|
|
instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE);
|
|
instance->rx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE);
|
|
instance->data = mf_ultralight_alloc();
|
|
|
|
instance->mfu_event.data = &instance->mfu_event_data;
|
|
|
|
instance->general_event.protocol = NfcProtocolMfUltralight;
|
|
instance->general_event.event_data = &instance->mfu_event;
|
|
instance->general_event.instance = instance;
|
|
|
|
return instance;
|
|
}
|
|
|
|
void mf_ultralight_poller_free(MfUltralightPoller* instance) {
|
|
furi_assert(instance);
|
|
furi_assert(instance->data);
|
|
furi_assert(instance->tx_buffer);
|
|
furi_assert(instance->rx_buffer);
|
|
|
|
bit_buffer_free(instance->tx_buffer);
|
|
bit_buffer_free(instance->rx_buffer);
|
|
mf_ultralight_free(instance->data);
|
|
free(instance);
|
|
}
|
|
|
|
static void mf_ultralight_poller_set_callback(
|
|
MfUltralightPoller* instance,
|
|
NfcGenericCallback callback,
|
|
void* context) {
|
|
furi_assert(instance);
|
|
furi_assert(callback);
|
|
|
|
instance->callback = callback;
|
|
instance->context = context;
|
|
}
|
|
|
|
const MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance) {
|
|
furi_assert(instance);
|
|
|
|
return instance->data;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance) {
|
|
bit_buffer_reset(instance->tx_buffer);
|
|
bit_buffer_reset(instance->rx_buffer);
|
|
iso14443_3a_copy(
|
|
instance->data->iso14443_3a_data,
|
|
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
|
|
instance->counters_read = 0;
|
|
instance->counters_total = 3;
|
|
instance->tearing_flag_read = 0;
|
|
instance->tearing_flag_total = 3;
|
|
instance->pages_read = 0;
|
|
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) {
|
|
FURI_LOG_D(TAG, "Read version success");
|
|
instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version);
|
|
instance->state = MfUltralightPollerStateGetFeatureSet;
|
|
} else {
|
|
FURI_LOG_D(TAG, "Didn't response. Check Ultralight C");
|
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
|
instance->state = MfUltralightPollerStateDetectMfulC;
|
|
}
|
|
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPoller* instance) {
|
|
instance->error = mf_ultralight_poller_authenticate(instance);
|
|
if(instance->error == MfUltralightErrorNone) {
|
|
FURI_LOG_D(TAG, "Ultralight C detected");
|
|
instance->data->type = MfUltralightTypeMfulC;
|
|
instance->state = MfUltralightPollerStateGetFeatureSet;
|
|
} else {
|
|
FURI_LOG_D(TAG, "Didn't response. Check NTAG 203");
|
|
instance->state = MfUltralightPollerStateDetectNtag203;
|
|
}
|
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
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) {
|
|
FURI_LOG_D(TAG, "NTAG203 detected");
|
|
instance->data->type = MfUltralightTypeNTAG203;
|
|
} else {
|
|
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 = next_state;
|
|
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_get_feature_set(MfUltralightPoller* instance) {
|
|
instance->feature_set = mf_ultralight_get_feature_support_set(instance->data->type);
|
|
instance->pages_total = mf_ultralight_get_pages_total(instance->data->type);
|
|
instance->data->pages_total = instance->pages_total;
|
|
FURI_LOG_D(
|
|
TAG,
|
|
"%s detected. Total pages: %d",
|
|
mf_ultralight_get_device_name(instance->data, NfcDeviceNameTypeFull),
|
|
instance->pages_total);
|
|
|
|
instance->state = MfUltralightPollerStateReadSignature;
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller* instance) {
|
|
MfUltralightPollerState next_state = MfUltralightPollerStateAuth;
|
|
if(mf_ultralight_support_feature(
|
|
instance->feature_set, MfUltralightFeatureSupportReadSignature)) {
|
|
FURI_LOG_D(TAG, "Reading signature");
|
|
instance->error =
|
|
mf_ultralight_poller_read_signature(instance, &instance->data->signature);
|
|
if(instance->error != MfUltralightErrorNone) {
|
|
FURI_LOG_D(TAG, "Read signature failed");
|
|
next_state = MfUltralightPollerStateReadFailed;
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Skip reading signature");
|
|
}
|
|
instance->state = next_state;
|
|
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller* instance) {
|
|
do {
|
|
if(!mf_ultralight_support_feature(
|
|
instance->feature_set, MfUltralightFeatureSupportReadCounter) ||
|
|
!mf_ultralight_is_counter_configured(instance->data)) {
|
|
FURI_LOG_D(TAG, "Skip reading counters");
|
|
instance->state = MfUltralightPollerStateReadTearingFlags;
|
|
break;
|
|
}
|
|
|
|
MfUltralightConfigPages* config = NULL;
|
|
mf_ultralight_get_config_page(instance->data, &config);
|
|
|
|
if(config->access.nfc_cnt_pwd_prot && !instance->auth_context.auth_success) {
|
|
FURI_LOG_D(TAG, "Counter reading is protected with password");
|
|
instance->state = MfUltralightPollerStateReadTearingFlags;
|
|
break;
|
|
}
|
|
|
|
if(instance->counters_read == instance->counters_total) {
|
|
instance->state = MfUltralightPollerStateReadTearingFlags;
|
|
break;
|
|
}
|
|
|
|
if(mf_ultralight_support_feature(
|
|
instance->feature_set, MfUltralightFeatureSupportSingleCounter)) {
|
|
instance->counters_read = 2;
|
|
}
|
|
|
|
FURI_LOG_D(TAG, "Reading counter %d", instance->counters_read);
|
|
instance->error = mf_ultralight_poller_read_counter(
|
|
instance, instance->counters_read, &instance->data->counter[instance->counters_read]);
|
|
if(instance->error != MfUltralightErrorNone) {
|
|
FURI_LOG_D(TAG, "Failed to read %d counter", instance->counters_read);
|
|
instance->state = MfUltralightPollerStateReadTearingFlags;
|
|
} else {
|
|
instance->counters_read++;
|
|
}
|
|
|
|
} while(false);
|
|
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) {
|
|
NfcCommand command = NfcCommandContinue;
|
|
|
|
if(mf_ultralight_support_feature(
|
|
instance->feature_set,
|
|
MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) {
|
|
if(instance->tearing_flag_read == instance->tearing_flag_total) {
|
|
instance->state = MfUltralightPollerStateTryDefaultPass;
|
|
command = NfcCommandReset;
|
|
} else {
|
|
bool single_counter = mf_ultralight_support_feature(
|
|
instance->feature_set, MfUltralightFeatureSupportSingleCounter);
|
|
if(single_counter) instance->tearing_flag_read = 2;
|
|
|
|
FURI_LOG_D(TAG, "Reading tearing flag %d", instance->tearing_flag_read);
|
|
instance->error = mf_ultralight_poller_read_tearing_flag(
|
|
instance,
|
|
instance->tearing_flag_read,
|
|
&instance->data->tearing_flag[instance->tearing_flag_read]);
|
|
if((instance->error == MfUltralightErrorProtocol) && single_counter) {
|
|
instance->tearing_flag_read++;
|
|
} else if(instance->error != MfUltralightErrorNone) {
|
|
FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read);
|
|
instance->state = MfUltralightPollerStateTryDefaultPass;
|
|
command = NfcCommandReset;
|
|
} else {
|
|
instance->tearing_flag_read++;
|
|
}
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Skip reading tearing flags");
|
|
instance->state = MfUltralightPollerStateTryDefaultPass;
|
|
command = NfcCommandReset;
|
|
}
|
|
|
|
return command;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) {
|
|
NfcCommand command = NfcCommandContinue;
|
|
if(mf_ultralight_support_feature(
|
|
instance->feature_set, MfUltralightFeatureSupportPasswordAuth)) {
|
|
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
|
|
|
|
command = instance->callback(instance->general_event, instance->context);
|
|
if(!instance->mfu_event.data->auth_context.skip_auth) {
|
|
instance->auth_context.password = instance->mfu_event.data->auth_context.password;
|
|
uint32_t pass = bit_lib_bytes_to_num_be(
|
|
instance->auth_context.password.data, sizeof(MfUltralightAuthPassword));
|
|
FURI_LOG_D(TAG, "Trying to authenticate with password %08lX", pass);
|
|
instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context);
|
|
if(instance->error == MfUltralightErrorNone) {
|
|
FURI_LOG_D(TAG, "Auth success");
|
|
instance->auth_context.auth_success = true;
|
|
instance->mfu_event.data->auth_context = instance->auth_context;
|
|
instance->mfu_event.type = MfUltralightPollerEventTypeAuthSuccess;
|
|
command = instance->callback(instance->general_event, instance->context);
|
|
} else {
|
|
FURI_LOG_D(TAG, "Auth failed");
|
|
instance->auth_context.auth_success = false;
|
|
instance->mfu_event.type = MfUltralightPollerEventTypeAuthFailed;
|
|
command = instance->callback(instance->general_event, instance->context);
|
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
|
}
|
|
}
|
|
}
|
|
instance->state = MfUltralightPollerStateReadPages;
|
|
|
|
return command;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) {
|
|
MfUltralightPageReadCommandData data = {};
|
|
uint16_t start_page = instance->pages_read;
|
|
if(MF_ULTRALIGHT_IS_NTAG_I2C(instance->data->type)) {
|
|
uint8_t tag = 0;
|
|
uint8_t sector = 0;
|
|
uint8_t pages_left = 0;
|
|
if(mf_ultralight_poller_ntag_i2c_addr_lin_to_tag(
|
|
instance, start_page, §or, &tag, &pages_left)) {
|
|
instance->error =
|
|
mf_ultralight_poller_read_page_from_sector(instance, sector, tag, &data);
|
|
} else {
|
|
FURI_LOG_D(TAG, "Failed to calculate sector and tag from %d page", start_page);
|
|
instance->error = MfUltralightErrorProtocol;
|
|
}
|
|
} else {
|
|
instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);
|
|
}
|
|
|
|
if(instance->error == MfUltralightErrorNone) {
|
|
for(size_t i = 0; i < 4; i++) {
|
|
if(start_page + i < instance->pages_total) {
|
|
FURI_LOG_D(TAG, "Read page %d success", start_page + i);
|
|
instance->data->page[start_page + i] = data.page[i];
|
|
instance->pages_read++;
|
|
instance->data->pages_read = instance->pages_read;
|
|
}
|
|
}
|
|
if(instance->pages_read == instance->pages_total) {
|
|
instance->state = MfUltralightPollerStateReadCounters;
|
|
}
|
|
} else {
|
|
FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read);
|
|
if(instance->pages_read) {
|
|
instance->state = MfUltralightPollerStateReadCounters;
|
|
} else {
|
|
instance->state = MfUltralightPollerStateReadFailed;
|
|
}
|
|
}
|
|
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoller* instance) {
|
|
do {
|
|
if(!mf_ultralight_support_feature(
|
|
instance->feature_set, MfUltralightFeatureSupportPasswordAuth))
|
|
break;
|
|
|
|
MfUltralightConfigPages* config = NULL;
|
|
mf_ultralight_get_config_page(instance->data, &config);
|
|
if(instance->auth_context.auth_success) {
|
|
config->password = instance->auth_context.password;
|
|
config->pack = instance->auth_context.pack;
|
|
} else if(config->access.authlim == 0) {
|
|
FURI_LOG_D(TAG, "No limits in authentication. Trying default password");
|
|
bit_lib_num_to_bytes_be(
|
|
MF_ULTRALIGHT_DEFAULT_PASSWORD,
|
|
sizeof(MfUltralightAuthPassword),
|
|
instance->auth_context.password.data);
|
|
instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context);
|
|
if(instance->error == MfUltralightErrorNone) {
|
|
FURI_LOG_D(TAG, "Default password detected");
|
|
bit_lib_num_to_bytes_be(
|
|
MF_ULTRALIGHT_DEFAULT_PASSWORD,
|
|
sizeof(MfUltralightAuthPassword),
|
|
config->password.data);
|
|
config->pack = instance->auth_context.pack;
|
|
instance->auth_context.auth_success = true;
|
|
}
|
|
}
|
|
|
|
if(instance->pages_read != instance->pages_total) {
|
|
// Probably password protected, fix AUTH0 and PROT so before AUTH0
|
|
// can be written and since AUTH0 won't be readable, like on the
|
|
// original card
|
|
config->auth0 = instance->pages_read;
|
|
config->access.prot = true;
|
|
} else if(!instance->auth_context.auth_success) {
|
|
instance->pages_read -= 2;
|
|
instance->data->pages_read -= 2;
|
|
}
|
|
} while(false);
|
|
|
|
instance->state = MfUltralightPollerStateReadSuccess;
|
|
return NfcCommandContinue;
|
|
}
|
|
|
|
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;
|
|
return command;
|
|
}
|
|
|
|
static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) {
|
|
FURI_LOG_D(TAG, "Read success");
|
|
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,
|
|
[MfUltralightPollerStateGetFeatureSet] = mf_ultralight_poller_handler_get_feature_set,
|
|
[MfUltralightPollerStateReadSignature] = mf_ultralight_poller_handler_read_signature,
|
|
[MfUltralightPollerStateReadCounters] = mf_ultralight_poller_handler_read_counters,
|
|
[MfUltralightPollerStateReadTearingFlags] =
|
|
mf_ultralight_poller_handler_read_tearing_flags,
|
|
[MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,
|
|
[MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,
|
|
[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,
|
|
|
|
};
|
|
|
|
static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) {
|
|
furi_assert(context);
|
|
furi_assert(event.event_data);
|
|
furi_assert(event.protocol == NfcProtocolIso14443_3a);
|
|
|
|
MfUltralightPoller* instance = context;
|
|
furi_assert(instance->callback);
|
|
|
|
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
|
|
|
|
NfcCommand command = NfcCommandContinue;
|
|
|
|
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
|
|
command = mf_ultralight_poller_read_handler[instance->state](instance);
|
|
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
|
|
instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed;
|
|
command = instance->callback(instance->general_event, instance->context);
|
|
}
|
|
|
|
return command;
|
|
}
|
|
|
|
static bool mf_ultralight_poller_detect(NfcGenericEvent event, void* context) {
|
|
furi_assert(context);
|
|
furi_assert(event.event_data);
|
|
furi_assert(event.protocol == NfcProtocolIso14443_3a);
|
|
|
|
bool protocol_detected = false;
|
|
MfUltralightPoller* instance = context;
|
|
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
|
|
|
|
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
|
|
MfUltralightPageReadCommandData read_page_cmd_data = {};
|
|
MfUltralightError error = mf_ultralight_poller_read_page(instance, 0, &read_page_cmd_data);
|
|
protocol_detected = (error == MfUltralightErrorNone);
|
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
|
}
|
|
|
|
return protocol_detected;
|
|
}
|
|
|
|
const NfcPollerBase mf_ultralight_poller = {
|
|
.alloc = (NfcPollerAlloc)mf_ultralight_poller_alloc,
|
|
.free = (NfcPollerFree)mf_ultralight_poller_free,
|
|
.set_callback = (NfcPollerSetCallback)mf_ultralight_poller_set_callback,
|
|
.run = (NfcPollerRun)mf_ultralight_poller_run,
|
|
.detect = (NfcPollerDetect)mf_ultralight_poller_detect,
|
|
.get_data = (NfcPollerGetData)mf_ultralight_poller_get_data,
|
|
};
|