unleashed-firmware/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c
gornekich d92b0a82cc
NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring.

Starring:

- @gornekich - NFC refactoring project lead, architect, senior developer
- @gsurkov - architect, senior developer
- @RebornedBrain - senior developer

Supporting roles:

- @skotopes, @DrZlo13, @hedger - general architecture advisors, code review
- @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance

Special thanks:

@bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
2023-10-24 12:08:09 +09:00

592 lines
22 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 = MfUltralightPollerStateReadVersion;
return NfcCommandContinue;
}
static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) {
instance->error = mf_ultralight_poller_async_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_async_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) {
MfUltralightPageReadCommandData data = {};
instance->error = mf_ultralight_poller_async_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;
}
instance->state = MfUltralightPollerStateGetFeatureSet;
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_async_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_async_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) {
if(mf_ultralight_support_feature(
instance->feature_set,
MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) {
if(instance->tearing_flag_read == instance->tearing_flag_total) {
instance->state = MfUltralightPollerStateTryDefaultPass;
} 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_async_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;
} else {
instance->tearing_flag_read++;
}
}
} else {
FURI_LOG_D(TAG, "Skip reading tearing flags");
instance->state = MfUltralightPollerStateTryDefaultPass;
}
return NfcCommandContinue;
}
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 = nfc_util_bytes2num(
instance->auth_context.password.data, sizeof(MfUltralightAuthPassword));
FURI_LOG_D(TAG, "Trying to authenticate with password %08lX", pass);
instance->error =
mf_ultralight_poller_async_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, &sector, &tag, &pages_left)) {
instance->error =
mf_ultralight_poller_async_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_async_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");
nfc_util_num2bytes(
MF_ULTRALIGHT_DEFAULT_PASSWORD,
sizeof(MfUltralightAuthPassword),
instance->auth_context.password.data);
instance->error =
mf_ultralight_poller_async_auth_pwd(instance, &instance->auth_context);
if(instance->error == MfUltralightErrorNone) {
FURI_LOG_D(TAG, "Default password detected");
nfc_util_num2bytes(
MF_ULTRALIGHT_DEFAULT_PASSWORD,
sizeof(MfUltralightAuthPassword),
config->password.data);
config->pack = instance->auth_context.pack;
}
}
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;
}
} 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.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");
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess;
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,
[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,
};
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_async_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,
};