1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00
Files
unleashed-firmware/lib/nfc/protocols/mf_classic/mf_classic_poller.c

1260 lines
50 KiB
C
Raw Normal View History

#include "mf_classic_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "MfClassicPoller"
#define MF_CLASSIC_MAX_BUFF_SIZE (64)
typedef NfcCommand (*MfClassicPollerReadHandler)(MfClassicPoller* instance);
MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {
furi_assert(iso14443_3a_poller);
MfClassicPoller* instance = malloc(sizeof(MfClassicPoller));
instance->iso14443_3a_poller = iso14443_3a_poller;
instance->data = mf_classic_alloc();
instance->crypto = crypto1_alloc();
instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->current_type_check = MfClassicType4k;
instance->card_state = MfClassicCardStateLost;
instance->mfc_event.data = &instance->mfc_event_data;
instance->general_event.protocol = NfcProtocolMfClassic;
instance->general_event.event_data = &instance->mfc_event;
instance->general_event.instance = instance;
return instance;
}
void mf_classic_poller_free(MfClassicPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->crypto);
furi_assert(instance->tx_plain_buffer);
furi_assert(instance->rx_plain_buffer);
furi_assert(instance->tx_encrypted_buffer);
furi_assert(instance->rx_encrypted_buffer);
mf_classic_free(instance->data);
crypto1_free(instance->crypto);
bit_buffer_free(instance->tx_plain_buffer);
bit_buffer_free(instance->rx_plain_buffer);
bit_buffer_free(instance->tx_encrypted_buffer);
bit_buffer_free(instance->rx_encrypted_buffer);
free(instance);
}
static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance) {
MfClassicPollerEventDataUpdate* data_update = &instance->mfc_event_data.data_update;
mf_classic_get_read_sectors_and_keys(
instance->data, &data_update->sectors_read, &data_update->keys_found);
data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector;
instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate;
return instance->callback(instance->general_event, instance->context);
}
static void mf_classic_poller_check_key_b_is_readable(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicBlock* data) {
do {
if(!mf_classic_is_sector_trailer(block_num)) break;
if(!mf_classic_is_allowed_access(
instance->data, block_num, MfClassicKeyTypeA, MfClassicActionKeyBRead))
break;
MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data;
uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, sizeof(MfClassicKey));
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
mf_classic_set_key_found(instance->data, sector_num, MfClassicKeyTypeB, key_b);
} while(false);
}
NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {
NfcCommand command = NfcCommandReset;
if(instance->current_type_check == MfClassicType4k) {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
MfClassicError error = mf_classic_poller_get_nt(instance, 254, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType4k;
instance->state = MfClassicPollerStateStart;
instance->current_type_check = MfClassicType4k;
FURI_LOG_D(TAG, "4K detected");
} else {
instance->current_type_check = MfClassicType1k;
}
} else if(instance->current_type_check == MfClassicType1k) {
MfClassicError error = mf_classic_poller_get_nt(instance, 62, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType1k;
FURI_LOG_D(TAG, "1K detected");
} else {
instance->data->type = MfClassicTypeMini;
FURI_LOG_D(TAG, "Mini detected");
}
instance->current_type_check = MfClassicType4k;
instance->state = MfClassicPollerStateStart;
}
return command;
}
NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type);
memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext));
instance->mfc_event.type = MfClassicPollerEventTypeRequestMode;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttack) {
mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);
instance->state = MfClassicPollerStateRequestKey;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) {
instance->state = MfClassicPollerStateRequestReadSector;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) {
instance->state = MfClassicPollerStateRequestSectorTrailer;
} else {
furi_crash("Invalid mode selected");
}
return command;
}
NfcCommand mf_classic_poller_handler_request_sector_trailer(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
if(write_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateSuccess;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeRequestSectorTrailer;
instance->mfc_event_data.sec_tr_data.sector_num = write_ctx->current_sector;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.sec_tr_data.sector_trailer_provided) {
instance->state = MfClassicPollerStateCheckWriteConditions;
memcpy(
&write_ctx->sec_tr,
&instance->mfc_event_data.sec_tr_data.sector_trailer,
sizeof(MfClassicSectorTrailer));
write_ctx->current_block =
MAX(1, mf_classic_get_first_block_num_of_sector(write_ctx->current_sector));
} else {
write_ctx->current_sector++;
}
}
return command;
}
NfcCommand mf_classic_handler_check_write_conditions(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
MfClassicSectorTrailer* sec_tr = &write_ctx->sec_tr;
do {
// Check last block in sector to write
uint8_t sec_tr_block_num =
mf_classic_get_sector_trailer_num_by_sector(write_ctx->current_sector);
if(write_ctx->current_block == sec_tr_block_num) {
write_ctx->current_sector++;
instance->state = MfClassicPollerStateRequestSectorTrailer;
break;
}
// Check write and read access
if(mf_classic_is_allowed_access_data_block(
sec_tr, write_ctx->current_block, MfClassicKeyTypeA, MfClassicActionDataWrite)) {
write_ctx->key_type_write = MfClassicKeyTypeA;
} else if(mf_classic_is_allowed_access_data_block(
sec_tr,
write_ctx->current_block,
MfClassicKeyTypeB,
MfClassicActionDataWrite)) {
write_ctx->key_type_write = MfClassicKeyTypeB;
} else if(mf_classic_is_value_block(sec_tr, write_ctx->current_block)) {
write_ctx->is_value_block = true;
} else {
FURI_LOG_D(TAG, "Not allowed to write block %d", write_ctx->current_block);
write_ctx->current_block++;
break;
}
if(mf_classic_is_allowed_access_data_block(
sec_tr,
write_ctx->current_block,
write_ctx->key_type_write,
MfClassicActionDataRead)) {
write_ctx->key_type_read = write_ctx->key_type_write;
} else {
write_ctx->key_type_read = write_ctx->key_type_write == MfClassicKeyTypeA ?
MfClassicKeyTypeB :
MfClassicKeyTypeA;
if(!mf_classic_is_allowed_access_data_block(
sec_tr,
write_ctx->current_block,
write_ctx->key_type_read,
MfClassicActionDataRead)) {
FURI_LOG_D(TAG, "Not allowed to read block %d", write_ctx->current_block);
write_ctx->current_block++;
break;
}
}
write_ctx->need_halt_before_write =
(write_ctx->key_type_read != write_ctx->key_type_write);
instance->state = MfClassicPollerStateReadBlock;
} while(false);
return command;
}
NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
MfClassicKey* auth_key = write_ctx->key_type_read == MfClassicKeyTypeA ?
&write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error = MfClassicErrorNone;
do {
// Authenticate to sector
error = mf_classic_poller_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
// Read block from tag
error = mf_classic_poller_read_block(
instance, write_ctx->current_block, &write_ctx->tag_block);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
if(write_ctx->is_value_block) {
mf_classic_poller_halt(instance);
instance->state = MfClassicPollerStateWriteValueBlock;
} else {
if(write_ctx->need_halt_before_write) {
mf_classic_poller_halt(instance);
}
instance->state = MfClassicPollerStateWriteBlock;
}
} while(false);
return command;
}
NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
MfClassicKey* auth_key = write_ctx->key_type_write == MfClassicKeyTypeA ?
&write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error = MfClassicErrorNone;
do {
// Request block to write
instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock;
instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfc_event_data.write_block_data.write_block_provided) break;
// Compare tag and saved block
if(memcmp(
write_ctx->tag_block.data,
instance->mfc_event_data.write_block_data.write_block.data,
sizeof(MfClassicBlock)) == 0) {
FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block);
break;
}
// Reauth if necessary
if(write_ctx->need_halt_before_write) {
error = mf_classic_poller_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL);
if(error != MfClassicErrorNone) {
FURI_LOG_D(
TAG, "Failed to auth to block %d for writing", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
}
// Write block
error = mf_classic_poller_write_block(
instance,
write_ctx->current_block,
&instance->mfc_event_data.write_block_data.write_block);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to write block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
} while(false);
mf_classic_poller_halt(instance);
write_ctx->current_block++;
instance->state = MfClassicPollerStateCheckWriteConditions;
return command;
}
NfcCommand mf_classic_poller_handler_write_value_block(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
do {
// Request block to write
instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock;
instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfc_event_data.write_block_data.write_block_provided) break;
// Compare tag and saved block
if(memcmp(
write_ctx->tag_block.data,
instance->mfc_event_data.write_block_data.write_block.data,
sizeof(MfClassicBlock)) == 0) {
FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block);
break;
}
bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeA,
MfClassicActionDataInc);
bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeB,
MfClassicActionDataInc);
bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeA,
MfClassicActionDataDec);
bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeB,
MfClassicActionDataDec);
int32_t source_value = 0;
int32_t target_value = 0;
if(!mf_classic_block_to_value(
&instance->mfc_event_data.write_block_data.write_block, &source_value, NULL))
break;
if(!mf_classic_block_to_value(&write_ctx->tag_block, &target_value, NULL)) break;
MfClassicKeyType auth_key_type = MfClassicKeyTypeA;
MfClassicValueCommand value_cmd = MfClassicValueCommandIncrement;
int32_t diff = source_value - target_value;
if(diff > 0) {
if(key_a_inc_allowed) {
auth_key_type = MfClassicKeyTypeA;
value_cmd = MfClassicValueCommandIncrement;
} else if(key_b_inc_allowed) {
auth_key_type = MfClassicKeyTypeB;
value_cmd = MfClassicValueCommandIncrement;
} else {
FURI_LOG_D(TAG, "Unable to increment value block");
break;
}
} else {
if(key_a_dec_allowed) {
auth_key_type = MfClassicKeyTypeA;
value_cmd = MfClassicValueCommandDecrement;
diff *= -1;
} else if(key_b_dec_allowed) {
auth_key_type = MfClassicKeyTypeB;
value_cmd = MfClassicValueCommandDecrement;
diff *= -1;
} else {
FURI_LOG_D(TAG, "Unable to decrement value block");
break;
}
}
MfClassicKey* key = (auth_key_type == MfClassicKeyTypeA) ? &write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error =
mf_classic_poller_auth(instance, write_ctx->current_block, key, auth_key_type, NULL);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_value_cmd(instance, write_ctx->current_block, value_cmd, diff);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_value_transfer(instance, write_ctx->current_block);
if(error != MfClassicErrorNone) break;
} while(false);
mf_classic_poller_halt(instance);
write_ctx->is_value_block = false;
write_ctx->current_block++;
instance->state = MfClassicPollerStateCheckWriteConditions;
return command;
}
NfcCommand mf_classic_poller_handler_request_read_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx;
MfClassicPollerEventDataReadSectorRequest* sec_read =
&instance->mfc_event_data.read_sector_request_data;
instance->mfc_event.type = MfClassicPollerEventTypeRequestReadSector;
command = instance->callback(instance->general_event, instance->context);
if(!sec_read->key_provided) {
instance->state = MfClassicPollerStateSuccess;
} else {
sec_read_ctx->current_sector = sec_read->sector_num;
sec_read_ctx->key = sec_read->key;
sec_read_ctx->key_type = sec_read->key_type;
sec_read_ctx->current_block =
mf_classic_get_first_block_num_of_sector(sec_read->sector_num);
sec_read_ctx->auth_passed = false;
instance->state = MfClassicPollerStateReadSectorBlocks;
}
return command;
}
NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx;
do {
MfClassicError error = MfClassicErrorNone;
if(!sec_read_ctx->auth_passed) {
uint64_t key = bit_lib_bytes_to_num_be(sec_read_ctx->key.data, sizeof(MfClassicKey));
FURI_LOG_D(
TAG,
"Auth to block %d with key %c: %06llx",
sec_read_ctx->current_block,
sec_read_ctx->key_type == MfClassicKeyTypeA ? 'A' : 'B',
key);
error = mf_classic_poller_auth(
instance,
sec_read_ctx->current_block,
&sec_read_ctx->key,
sec_read_ctx->key_type,
NULL);
if(error != MfClassicErrorNone) break;
sec_read_ctx->auth_passed = true;
if(!mf_classic_is_key_found(
instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type)) {
mf_classic_set_key_found(
instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type, key);
}
}
if(mf_classic_is_block_read(instance->data, sec_read_ctx->current_block)) break;
FURI_LOG_D(TAG, "Reading block %d", sec_read_ctx->current_block);
MfClassicBlock read_block = {};
error = mf_classic_poller_read_block(instance, sec_read_ctx->current_block, &read_block);
if(error == MfClassicErrorNone) {
mf_classic_set_block_read(instance->data, sec_read_ctx->current_block, &read_block);
if(sec_read_ctx->key_type == MfClassicKeyTypeA) {
mf_classic_poller_check_key_b_is_readable(
instance, sec_read_ctx->current_block, &read_block);
}
} else {
mf_classic_poller_halt(instance);
sec_read_ctx->auth_passed = false;
}
} while(false);
uint8_t sec_tr_num = mf_classic_get_sector_trailer_num_by_sector(sec_read_ctx->current_sector);
sec_read_ctx->current_block++;
if(sec_read_ctx->current_block > sec_tr_num) {
mf_classic_poller_halt(instance);
instance->state = MfClassicPollerStateRequestReadSector;
}
return command;
}
NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
instance->mfc_event.type = MfClassicPollerEventTypeRequestKey;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.key_request_data.key_provided) {
dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key;
instance->state = MfClassicPollerStateAuthKeyA;
} else {
instance->state = MfClassicPollerStateNextSector;
}
return command;
}
NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateAuthKeyB;
} else {
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);
uint64_t key =
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateReadSector;
} else {
mf_classic_poller_halt(instance);
instance->state = MfClassicPollerStateAuthKeyB;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) {
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateNextSector;
} else {
instance->state = MfClassicPollerStateRequestKey;
}
} else {
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);
uint64_t key =
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeB;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateReadSector;
} else {
mf_classic_poller_halt(instance);
instance->state = MfClassicPollerStateRequestKey;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
dict_attack_ctx->current_sector++;
if(dict_attack_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateSuccess;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeNextSector;
instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateRequestKey;
}
return command;
}
NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicError error = MfClassicErrorNone;
uint8_t block_num = dict_attack_ctx->current_block;
MfClassicBlock block = {};
do {
if(mf_classic_is_block_read(instance->data, block_num)) break;
if(!dict_attack_ctx->auth_passed) {
error = mf_classic_poller_auth(
instance,
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateNextSector;
FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector");
break;
}
}
FURI_LOG_D(TAG, "Reading block %d", block_num);
error = mf_classic_poller_read_block(instance, block_num, &block);
if(error != MfClassicErrorNone) {
mf_classic_poller_halt(instance);
dict_attack_ctx->auth_passed = false;
FURI_LOG_D(TAG, "Failed to read block %d", block_num);
} else {
mf_classic_set_block_read(instance->data, block_num, &block);
if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {
mf_classic_poller_check_key_b_is_readable(instance, block_num, &block);
}
}
} while(false);
uint8_t sec_tr_block_num =
mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->current_sector);
dict_attack_ctx->current_block++;
if(dict_attack_ctx->current_block > sec_tr_block_num) {
mf_classic_poller_handle_data_update(instance);
mf_classic_poller_halt(instance);
dict_attack_ctx->auth_passed = false;
if(dict_attack_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateNextSector;
} else {
dict_attack_ctx->reuse_key_sector = dict_attack_ctx->current_sector;
instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;
instance->mfc_event_data.key_attack_data.current_sector =
dict_attack_ctx->reuse_key_sector;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateKeyReuseStart;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_start(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
2024-08-01 09:10:02 -04:00
do {
if(!dict_attack_ctx->prng_type) {
instance->state = MfClassicPollerStateNestedAnalyzePRNG;
break;
}
if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {
dict_attack_ctx->current_key_type = MfClassicKeyTypeB;
instance->state = MfClassicPollerStateKeyReuseAuthKeyB;
} else {
2024-08-01 09:10:02 -04:00
dict_attack_ctx->reuse_key_sector++;
if(dict_attack_ctx->reuse_key_sector == instance->sectors_total) {
instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateRequestKey;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;
instance->mfc_event_data.key_attack_data.current_sector =
dict_attack_ctx->reuse_key_sector;
command = instance->callback(instance->general_event, instance->context);
2024-08-01 09:10:02 -04:00
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
instance->state = MfClassicPollerStateKeyReuseAuthKeyA;
}
}
2024-08-01 09:10:02 -04:00
} while(false);
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateKeyReuseStart;
} else {
uint8_t block =
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint64_t key =
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateKeyReuseReadSector;
} else {
mf_classic_poller_halt(instance);
dict_attack_ctx->auth_passed = false;
instance->state = MfClassicPollerStateKeyReuseStart;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB)) {
instance->state = MfClassicPollerStateKeyReuseStart;
} else {
uint8_t block =
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint64_t key =
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeB;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateKeyReuseReadSector;
} else {
mf_classic_poller_halt(instance);
dict_attack_ctx->auth_passed = false;
instance->state = MfClassicPollerStateKeyReuseStart;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicError error = MfClassicErrorNone;
uint8_t block_num = dict_attack_ctx->current_block;
MfClassicBlock block = {};
do {
if(mf_classic_is_block_read(instance->data, block_num)) break;
if(!dict_attack_ctx->auth_passed) {
error = mf_classic_poller_auth(
instance,
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateKeyReuseStart;
break;
}
}
FURI_LOG_D(TAG, "Reading block %d", block_num);
error = mf_classic_poller_read_block(instance, block_num, &block);
if(error != MfClassicErrorNone) {
mf_classic_poller_halt(instance);
dict_attack_ctx->auth_passed = false;
FURI_LOG_D(TAG, "Failed to read block %d", block_num);
} else {
mf_classic_set_block_read(instance->data, block_num, &block);
if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {
mf_classic_poller_check_key_b_is_readable(instance, block_num, &block);
}
}
} while(false);
uint16_t sec_tr_block_num =
mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->reuse_key_sector);
dict_attack_ctx->current_block++;
if(dict_attack_ctx->current_block > sec_tr_block_num) {
mf_classic_poller_halt(instance);
dict_attack_ctx->auth_passed = false;
mf_classic_poller_handle_data_update(instance);
instance->state = MfClassicPollerStateKeyReuseStart;
}
return command;
}
2024-08-01 09:10:02 -04:00
void nonce_distance(uint32_t* msb, uint32_t* lsb) {
uint16_t x = 1, pos;
uint8_t calc_ok = 0;
for(uint16_t i = 1; i; ++i) {
pos = (x & 0xff) << 8 | x >> 8;
if((pos == *msb) & !(calc_ok >> 0 & 0x01)) {
*msb = i;
calc_ok |= 0x01;
}
if((pos == *lsb) & !(calc_ok >> 1 & 0x01)) {
*lsb = i;
calc_ok |= 0x02;
}
if(calc_ok == 0x03) {
return;
}
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
}
}
// TODO: Faster? https://github.com/RfidResearchGroup/proxmark3/commit/bd3e8db852186d4ab9d5dda890d1cd52389b1254
bool validate_prng_nonce(uint32_t nonce) {
uint32_t msb = nonce >> 16;
uint32_t lsb = nonce & 0xffff;
nonce_distance(&msb, &lsb);
return ((65535 - msb + lsb) % 65535) == 16;
}
// Helper function to add a nonce to the array
static bool add_nested_nonce(MfClassicNestedNonceArray* array, uint32_t cuid, uint8_t key_idx, uint32_t nt, uint32_t nt_enc, uint8_t par, uint16_t dist) {
MfClassicNestedNonce* new_nonces = realloc(array->nonces, (array->count + 1) * sizeof(MfClassicNestedNonce));
if (new_nonces == NULL) return false;
array->nonces = new_nonces;
array->nonces[array->count].cuid = cuid;
array->nonces[array->count].key_idx = key_idx;
array->nonces[array->count].nt = nt;
array->nonces[array->count].nt_enc = nt_enc;
array->nonces[array->count].par = par;
array->nonces[array->count].dist = dist;
array->count++;
return true;
}
NfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
// Analyze PRNG by collecting nt
uint8_t nonce_limit = 10;
if (dict_attack_ctx->nt_count > 0) {
if(!validate_prng_nonce(dict_attack_ctx->nt_prev)) dict_attack_ctx->hard_nt_count++;
}
if(dict_attack_ctx->nt_count <= nonce_limit) {
instance->state = MfClassicPollerStateNestedCollectNt;
return command;
}
if(dict_attack_ctx->hard_nt_count > 3) {
dict_attack_ctx->prng_type = MfClassicPrngTypeHard;
// FIXME: E -> D
FURI_LOG_E(TAG, "Detected Hard PRNG");
} else {
dict_attack_ctx->prng_type = MfClassicPrngTypeWeak;
// FIXME: E -> D
FURI_LOG_E(TAG, "Detected Weak PRNG");
}
instance->state = MfClassicPollerStateNestedController;
return command;
}
NfcCommand mf_classic_poller_handler_nested_analyze_backdoor(MfClassicPoller* instance) {
// Stub
NfcCommand command = NfcCommandContinue;
return command;
}
NfcCommand mf_classic_poller_handler_nested_collect_nt(MfClassicPoller* instance) {
NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicNt nt = {};
MfClassicError error = mf_classic_poller_get_nt(instance, 0, MfClassicKeyTypeA, &nt);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateKeyReuseStart;
dict_attack_ctx->prng_type = MfClassicPrngTypeNoTag;
// FIXME: E -> D
FURI_LOG_E(TAG, "Failed to collect nt");
} else {
// FIXME: E -> D
FURI_LOG_E(TAG, "nt: %02x%02x%02x%02x", nt.data[0], nt.data[1], nt.data[2], nt.data[3]);
uint32_t nt_data = bit_lib_bytes_to_num_be(nt.data, sizeof(MfClassicNt));
dict_attack_ctx->nt_prev = nt_data;
dict_attack_ctx->nt_count++;
instance->state = MfClassicPollerStateNestedAnalyzePRNG;
}
return command;
}
NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* instance) {
// TODO: Collect parity
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicNestedNonceArray result = {NULL, 0};
do {
if (dict_attack_ctx->prng_type == MfClassicPrngTypeHard) {
FURI_LOG_E(TAG, "Hard PRNG, skipping calibration");
// TODO: Collect hardnested nonces (cuid, sec, par, nt_enc, A/B)
// https://github.com/AloneLiberty/FlipperNested/blob/eba6163d7ef22adef5f9fe4d77a78ccfcc27a952/lib/nested/nested.c#L564-L645
break;
}
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error;
uint32_t davg = 0, dmin = 0, dmax = 0;
uint32_t collected = 0;
const uint32_t rounds = 10;
uint32_t nt_enc_arr[rounds];
uint32_t nt_prev = 0;
uint32_t nt_enc_prev = 0;
uint32_t same_nt_enc_cnt = 0;
uint32_t dist = 0;
bool static_encrypted = false;
// Step 1: Perform full authentication once
error = mf_classic_poller_auth(
instance,
block,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
&auth_ctx);
if (error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform full authentication");
break;
}
FURI_LOG_E(TAG, "Full authentication successful");
nt_prev = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
// Step 2: Perform nested authentication multiple times
for (uint32_t round_no = 0; round_no < rounds; round_no++) {
error = mf_classic_poller_auth_nested(
instance,
block,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
&auth_ctx);
if (error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform nested authentication %lu", round_no);
continue;
}
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
if (nt_enc == nt_enc_prev) {
same_nt_enc_cnt++;
if (same_nt_enc_cnt > 3) {
static_encrypted = true;
break;
}
} else {
same_nt_enc_cnt = 0;
nt_enc_prev = nt_enc;
}
nt_enc_arr[round_no] = nt_enc;
}
if (static_encrypted) {
FURI_LOG_E(TAG, "Static encrypted nonce detected");
if (dict_attack_ctx->backdoor == MfClassicBackdoorFM11RF08S) {
// TODO: Backdoor static nested attack
break;
} else {
// TODO: If not present, just log nonces with parity bits
bool success = add_nested_nonce(&result, cuid, dict_attack_ctx->reuse_key_sector, nt_prev, nt_enc_prev, 0, 0);
if (!success) {
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
}
break;
}
}
// Find the distance between each nonce
// TODO: Distance calculation
uint64_t known_key = bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, 6);
Crypto1 crypto;
crypto1_init(&crypto, known_key);
for (uint32_t rtr = 0; rtr < rounds; rtr++) {
bool found = false;
for (int i = 0; i < 65535; i++) {
Crypto1 crypto_temp = {.odd = crypto.odd, .even = crypto.even};
uint32_t nth_successor = prng_successor(nt_prev, i);
if ((nth_successor ^ crypto1_word(&crypto_temp, cuid ^ nth_successor, 0)) == nt_enc_arr[rtr]) {
FURI_LOG_E(TAG, "nt_enc (plain) %08lx", nth_successor);
FURI_LOG_E(TAG, "dist from nt prev: %i", i);
nt_prev = nth_successor;
found = true;
/*
dist_a = i;
found_dist++;
printf("ks: %08x\n", nth_successor ^ nt_enc);
*/
break;
}
}
if (!found) {
FURI_LOG_E(TAG, "Failed to find distance for nt_enc %08lx", nt_enc_arr[rtr]);
FURI_LOG_E(TAG, "using key %06llx and uid %08lx, nt_prev is %08lx", known_key, cuid, nt_prev);
}
}
if (collected > 0) {
davg /= collected;
FURI_LOG_E(
TAG,
"Calibration completed: min=%lu max=%lu avg=%lu collected=%lu",
dmin,
dmax,
davg,
collected);
} else {
FURI_LOG_E(TAG, "Failed to collect any valid nonce distances");
}
} while(false);
instance->state = MfClassicPollerStateNestedController;
mf_classic_poller_halt(instance);
return command;
}
NfcCommand mf_classic_poller_handler_nested_log(MfClassicPoller* instance) {
// Log collected nonces to /ext/nfc/.nested.log
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
size_t nonce_idx = dict_attack_ctx->nested_nonce.count;
while (nonce_idx > 0) {
// TODO: Make sure we're not off by one here
FURI_LOG_E(TAG, "nt: %08lx", dict_attack_ctx->nested_nonce.nonces[nonce_idx].nt);
nonce_idx--;
}
free(dict_attack_ctx->nested_nonce.nonces);
return command;
}
NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) {
// Iterate through keys
// To run an accelerated dictionary attack, first we need static nested nonces.
// If the PRNG is hard, we can't run an accelerated dictionary attack.
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if (dict_attack_ctx->nested_nonce.count > 0) {
instance->state = MfClassicPollerStateNestedLog;
return command;
}
if (dict_attack_ctx->backdoor == MfClassicBackdoorUnknown) {
instance->state = MfClassicPollerStateNestedAnalyzeBackdoor;
return command;
}
// Target all sectors, key A and B
for (uint8_t key_idx = 0; key_idx <= (instance->sectors_total * 2); key_idx++) {
dict_attack_ctx->nested_target_key = key_idx;
instance->state = MfClassicPollerStateNestedCollectNtEnc;
return command;
}
// TODO: If we've recovered all keys, read blocks and go to complete
instance->state = MfClassicPollerStateKeyReuseStart;
return command;
}
NfcCommand mf_classic_poller_handler_success(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->mfc_event.type = MfClassicPollerEventTypeSuccess;
command = instance->callback(instance->general_event, instance->context);
return command;
}
NfcCommand mf_classic_poller_handler_fail(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->mfc_event.type = MfClassicPollerEventTypeFail;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateDetectType;
return command;
}
static const MfClassicPollerReadHandler
mf_classic_poller_dict_attack_handler[MfClassicPollerStateNum] = {
[MfClassicPollerStateDetectType] = mf_classic_poller_handler_detect_type,
[MfClassicPollerStateStart] = mf_classic_poller_handler_start,
[MfClassicPollerStateRequestSectorTrailer] =
mf_classic_poller_handler_request_sector_trailer,
[MfClassicPollerStateCheckWriteConditions] = mf_classic_handler_check_write_conditions,
[MfClassicPollerStateReadBlock] = mf_classic_poller_handler_read_block,
[MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block,
[MfClassicPollerStateWriteValueBlock] = mf_classic_poller_handler_write_value_block,
[MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector,
[MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key,
[MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector,
[MfClassicPollerStateReadSectorBlocks] =
mf_classic_poller_handler_request_read_sector_blocks,
[MfClassicPollerStateAuthKeyA] = mf_classic_poller_handler_auth_a,
[MfClassicPollerStateAuthKeyB] = mf_classic_poller_handler_auth_b,
[MfClassicPollerStateReadSector] = mf_classic_poller_handler_read_sector,
[MfClassicPollerStateKeyReuseStart] = mf_classic_poller_handler_key_reuse_start,
[MfClassicPollerStateKeyReuseAuthKeyA] = mf_classic_poller_handler_key_reuse_auth_key_a,
[MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b,
[MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector,
2024-08-01 09:10:02 -04:00
[MfClassicPollerStateNestedAnalyzePRNG] = mf_classic_poller_handler_nested_analyze_prng,
[MfClassicPollerStateNestedAnalyzeBackdoor] = mf_classic_poller_handler_nested_analyze_backdoor,
[MfClassicPollerStateNestedCollectNt] =
mf_classic_poller_handler_nested_collect_nt,
[MfClassicPollerStateNestedController] =
mf_classic_poller_handler_nested_controller,
[MfClassicPollerStateNestedCollectNtEnc] =
mf_classic_poller_handler_nested_collect_nt_enc,
[MfClassicPollerStateNestedLog] = mf_classic_poller_handler_nested_log,
[MfClassicPollerStateSuccess] = mf_classic_poller_handler_success,
[MfClassicPollerStateFail] = mf_classic_poller_handler_fail,
};
NfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(context);
MfClassicPoller* instance = context;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
if(instance->card_state == MfClassicCardStateLost) {
instance->card_state = MfClassicCardStateDetected;
instance->mfc_event.type = MfClassicPollerEventTypeCardDetected;
instance->callback(instance->general_event, instance->context);
}
command = mf_classic_poller_dict_attack_handler[instance->state](instance);
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
if(instance->card_state == MfClassicCardStateDetected) {
instance->card_state = MfClassicCardStateLost;
instance->mfc_event.type = MfClassicPollerEventTypeCardLost;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
bool mf_classic_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(context);
Iso14443_3aPoller* iso3_poller = event.instance;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
bool detected = false;
const uint8_t auth_cmd[] = {MF_CLASSIC_CMD_AUTH_KEY_A, 0};
BitBuffer* tx_buffer = bit_buffer_alloc(COUNT_OF(auth_cmd));
bit_buffer_copy_bytes(tx_buffer, auth_cmd, COUNT_OF(auth_cmd));
BitBuffer* rx_buffer = bit_buffer_alloc(sizeof(MfClassicNt));
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
iso3_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);
if(error == Iso14443_3aErrorWrongCrc) {
if(bit_buffer_get_size_bytes(rx_buffer) == sizeof(MfClassicNt)) {
detected = true;
}
}
}
bit_buffer_free(tx_buffer);
bit_buffer_free(rx_buffer);
return detected;
}
void mf_classic_poller_set_callback(
MfClassicPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
const MfClassicData* mf_classic_poller_get_data(const MfClassicPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
const NfcPollerBase mf_classic_poller = {
.alloc = (NfcPollerAlloc)mf_classic_poller_alloc,
.free = (NfcPollerFree)mf_classic_poller_free,
.set_callback = (NfcPollerSetCallback)mf_classic_poller_set_callback,
.run = (NfcPollerRun)mf_classic_poller_run,
.detect = (NfcPollerDetect)mf_classic_poller_detect,
.get_data = (NfcPollerGetData)mf_classic_poller_get_data,
2024-08-01 09:10:02 -04:00
};