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 07:08:09 +04:00
|
|
|
#include "mf_classic_poller_i.h"
|
|
|
|
|
|
|
|
|
|
#include <nfc/protocols/nfc_poller_base.h>
|
|
|
|
|
|
|
|
|
|
#include <furi.h>
|
2024-08-01 22:16:10 -04:00
|
|
|
#include <stream/stream.h>
|
|
|
|
|
#include <stream/buffered_file_stream.h>
|
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 07:08:09 +04:00
|
|
|
|
|
|
|
|
#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;
|
2024-01-12 17:41:19 +09:00
|
|
|
instance->card_state = MfClassicCardStateLost;
|
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 07:08:09 +04:00
|
|
|
|
|
|
|
|
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;
|
2024-02-14 13:41:42 +09:00
|
|
|
uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, sizeof(MfClassicKey));
|
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 07:08:09 +04:00
|
|
|
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));
|
2023-11-15 12:32:45 +04:00
|
|
|
MfClassicError error = mf_classic_poller_get_nt(instance, 254, MfClassicKeyTypeA, NULL);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
MfClassicError error = mf_classic_poller_get_nt(instance, 62, MfClassicKeyTypeA, NULL);
|
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 07:08:09 +04:00
|
|
|
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
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_read_block(
|
|
|
|
|
instance, write_ctx->current_block, &write_ctx->tag_block);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
instance->state = MfClassicPollerStateWriteValueBlock;
|
|
|
|
|
} else {
|
|
|
|
|
if(write_ctx->need_halt_before_write) {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
}
|
|
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_write_block(
|
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 07:08:09 +04:00
|
|
|
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);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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 =
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_auth(instance, write_ctx->current_block, key, auth_key_type, NULL);
|
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 07:08:09 +04:00
|
|
|
if(error != MfClassicErrorNone) break;
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_value_cmd(instance, write_ctx->current_block, value_cmd, diff);
|
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 07:08:09 +04:00
|
|
|
if(error != MfClassicErrorNone) break;
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_value_transfer(instance, write_ctx->current_block);
|
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 07:08:09 +04:00
|
|
|
if(error != MfClassicErrorNone) break;
|
|
|
|
|
|
|
|
|
|
} while(false);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2024-02-14 13:41:42 +09:00
|
|
|
uint64_t key = bit_lib_bytes_to_num_be(sec_read_ctx->key.data, sizeof(MfClassicKey));
|
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 07:08:09 +04:00
|
|
|
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);
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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 = {};
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_read_block(instance, sec_read_ctx->current_block, &read_block);
|
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 07:08:09 +04:00
|
|
|
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 {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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);
|
2024-02-14 13:41:42 +09:00
|
|
|
uint64_t key =
|
|
|
|
|
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
|
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 07:08:09 +04:00
|
|
|
FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
MfClassicError error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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 {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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);
|
2024-02-14 13:41:42 +09:00
|
|
|
uint64_t key =
|
|
|
|
|
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
|
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 07:08:09 +04:00
|
|
|
FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
MfClassicError error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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 {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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);
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_read_block(instance, block_num, &block);
|
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 07:08:09 +04:00
|
|
|
|
|
|
|
|
if(error != MfClassicErrorNone) {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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;
|
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 07:08:09 +04:00
|
|
|
} 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);
|
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 07:08:09 +04:00
|
|
|
|
2024-08-01 09:10:02 -04:00
|
|
|
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
|
|
|
|
|
instance->state = MfClassicPollerStateKeyReuseAuthKeyA;
|
|
|
|
|
}
|
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 07:08:09 +04:00
|
|
|
}
|
2024-08-01 09:10:02 -04:00
|
|
|
} while(false);
|
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 07:08:09 +04:00
|
|
|
|
|
|
|
|
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);
|
2024-02-14 13:41:42 +09:00
|
|
|
uint64_t key =
|
|
|
|
|
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
|
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 07:08:09 +04:00
|
|
|
FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
MfClassicError error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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 {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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);
|
2024-02-14 13:41:42 +09:00
|
|
|
uint64_t key =
|
|
|
|
|
bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
|
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 07:08:09 +04:00
|
|
|
FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key);
|
|
|
|
|
|
2023-11-15 12:32:45 +04:00
|
|
|
MfClassicError error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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 {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_auth(
|
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 07:08:09 +04:00
|
|
|
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);
|
2023-11-15 12:32:45 +04:00
|
|
|
error = mf_classic_poller_read_block(instance, block_num, &block);
|
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 07:08:09 +04:00
|
|
|
|
|
|
|
|
if(error != MfClassicErrorNone) {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
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) {
|
2023-11-15 12:32:45 +04:00
|
|
|
mf_classic_poller_halt(instance);
|
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 07:08:09 +04:00
|
|
|
dict_attack_ctx->auth_passed = false;
|
|
|
|
|
|
|
|
|
|
mf_classic_poller_handle_data_update(instance);
|
|
|
|
|
instance->state = MfClassicPollerStateKeyReuseStart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
bool validate_prng_nonce(uint32_t nonce) {
|
|
|
|
|
if(nonce == 0) return false;
|
|
|
|
|
uint16_t x = nonce >> 16;
|
|
|
|
|
x = (x & 0xff) << 8 | x >> 8;
|
|
|
|
|
for(uint8_t i = 0; i < 16; i++) {
|
2024-08-01 09:10:02 -04:00
|
|
|
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
|
|
|
|
|
}
|
2024-08-03 21:35:52 -04:00
|
|
|
x = (x & 0xff) << 8 | x >> 8;
|
|
|
|
|
return x == (nonce & 0xFFFF);
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper function to add a nonce to the array
|
2024-08-03 21:35:52 -04:00
|
|
|
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;
|
2024-08-01 09:10:02 -04:00
|
|
|
|
|
|
|
|
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
|
2024-08-04 23:53:21 -04:00
|
|
|
uint8_t nonce_limit = 5;
|
2024-08-01 09:10:02 -04:00
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
if(dict_attack_ctx->nt_count > 0) {
|
2024-08-01 09:10:02 -04:00
|
|
|
if(!validate_prng_nonce(dict_attack_ctx->nt_prev)) dict_attack_ctx->hard_nt_count++;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
if(dict_attack_ctx->nt_count < nonce_limit) {
|
2024-08-01 09:10:02 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedCollectNt;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-04 23:53:21 -04:00
|
|
|
if(dict_attack_ctx->hard_nt_count >= 3) {
|
2024-08-01 09:10:02 -04:00
|
|
|
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) {
|
|
|
|
|
NfcCommand command = NfcCommandContinue;
|
2024-08-01 22:16:10 -04:00
|
|
|
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
|
|
|
|
// TODO: Check for Fudan backdoor
|
2024-08-09 20:22:19 -04:00
|
|
|
// Can use on more than S variant as a free key for Nested
|
|
|
|
|
/*
|
|
|
|
|
do {
|
|
|
|
|
uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
|
|
|
|
|
MF_CLASSIC_CMD_AUTH_KEY_A;
|
|
|
|
|
uint8_t auth_cmd[2] = {auth_type, block_num};
|
|
|
|
|
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
|
|
|
|
|
|
|
|
|
|
if(is_nested) {
|
|
|
|
|
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
|
|
|
|
|
crypto1_encrypt(
|
|
|
|
|
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
|
|
|
|
|
error = iso14443_3a_poller_txrx_custom_parity(
|
|
|
|
|
instance->iso14443_3a_poller,
|
|
|
|
|
instance->tx_encrypted_buffer,
|
|
|
|
|
instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth
|
|
|
|
|
MF_CLASSIC_FWT_FC);
|
|
|
|
|
if(error != Iso14443_3aErrorNone) {
|
|
|
|
|
ret = mf_classic_process_error(error);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
*/
|
2024-08-01 22:16:10 -04:00
|
|
|
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
|
2024-08-03 21:35:52 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedController;
|
2024-08-01 09:10:02 -04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-04 23:53:21 -04:00
|
|
|
void bubble_sort(uint16_t arr[], int n) {
|
|
|
|
|
for(int i = 0; i < n - 1; i++) {
|
|
|
|
|
for(int j = 0; j < n - i - 1; j++) {
|
|
|
|
|
if(arr[j] > arr[j + 1]) {
|
|
|
|
|
uint16_t temp = arr[j];
|
|
|
|
|
arr[j] = arr[j + 1];
|
|
|
|
|
arr[j + 1] = temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t get_median(uint16_t arr[], int n) {
|
|
|
|
|
bubble_sort(arr, n);
|
|
|
|
|
return arr[n / 2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) {
|
|
|
|
|
// TODO: Calibrate backdoored tags too
|
2024-08-09 20:22:19 -04:00
|
|
|
// TODO: Check if we have already identified the tag as static encrypted
|
2024-08-04 23:53:21 -04:00
|
|
|
NfcCommand command = NfcCommandContinue;
|
|
|
|
|
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
|
|
|
|
uint8_t nt_enc_calibration_cnt = 11;
|
|
|
|
|
uint32_t nt_enc_temp_arr[nt_enc_calibration_cnt];
|
|
|
|
|
|
|
|
|
|
uint16_t d_min = UINT16_MAX;
|
|
|
|
|
uint16_t d_max = 0;
|
|
|
|
|
uint16_t d_all[nt_enc_calibration_cnt - 1];
|
|
|
|
|
uint8_t d_all_cnt = 0;
|
|
|
|
|
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 nt_prev = 0;
|
|
|
|
|
uint32_t nt_enc_prev = 0;
|
|
|
|
|
uint32_t same_nt_enc_cnt = 0;
|
2024-08-09 20:22:19 -04:00
|
|
|
uint8_t nt_enc_collected = 0;
|
2024-08-04 23:53:21 -04:00
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
|
instance->state = MfClassicPollerStateNestedCalibrate;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(uint8_t collection_cycle = 0; collection_cycle < nt_enc_calibration_cnt;
|
|
|
|
|
collection_cycle++) {
|
|
|
|
|
error = mf_classic_poller_auth_nested(
|
|
|
|
|
instance,
|
|
|
|
|
block,
|
|
|
|
|
&dict_attack_ctx->current_key,
|
|
|
|
|
dict_attack_ctx->current_key_type,
|
2024-08-09 20:22:19 -04:00
|
|
|
&auth_ctx,
|
|
|
|
|
false);
|
2024-08-04 23:53:21 -04:00
|
|
|
|
|
|
|
|
if(error != MfClassicErrorNone) {
|
|
|
|
|
FURI_LOG_E(TAG, "Failed to perform nested authentication %u", collection_cycle);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
nt_enc_temp_arr[collection_cycle] =
|
|
|
|
|
bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
|
|
|
|
|
nt_enc_collected++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < nt_enc_collected; i++) {
|
|
|
|
|
if(nt_enc_temp_arr[i] == nt_enc_prev) {
|
2024-08-04 23:53:21 -04:00
|
|
|
same_nt_enc_cnt++;
|
|
|
|
|
if(same_nt_enc_cnt > 3) {
|
2024-08-09 20:22:19 -04:00
|
|
|
dict_attack_ctx->static_encrypted = true;
|
2024-08-04 23:53:21 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
same_nt_enc_cnt = 0;
|
2024-08-09 20:22:19 -04:00
|
|
|
nt_enc_prev = nt_enc_temp_arr[i];
|
2024-08-04 23:53:21 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
if(dict_attack_ctx->static_encrypted) {
|
2024-08-04 23:53:21 -04:00
|
|
|
FURI_LOG_E(TAG, "Static encrypted nonce detected");
|
|
|
|
|
if(dict_attack_ctx->backdoor == MfClassicBackdoorFM11RF08S) {
|
|
|
|
|
// TODO: Backdoor static nested attack calibration
|
|
|
|
|
dict_attack_ctx->calibrated = true;
|
|
|
|
|
instance->state = MfClassicPollerStateNestedController;
|
|
|
|
|
return command;
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: Log these
|
|
|
|
|
dict_attack_ctx->calibrated = true;
|
|
|
|
|
instance->state = MfClassicPollerStateNestedController;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the distance between each nonce
|
|
|
|
|
FURI_LOG_E(TAG, "Calculating distance between nonces");
|
|
|
|
|
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 collection_cycle = 0; collection_cycle < nt_enc_calibration_cnt;
|
|
|
|
|
collection_cycle++) {
|
|
|
|
|
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_temp_arr[collection_cycle]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(collection_cycle > 0) {
|
|
|
|
|
FURI_LOG_E(TAG, "nt_enc (plain) %08lx", nth_successor);
|
|
|
|
|
FURI_LOG_E(TAG, "dist from nt prev: %i", i);
|
|
|
|
|
d_all[d_all_cnt++] = i;
|
|
|
|
|
if(i < d_min) d_min = i;
|
|
|
|
|
if(i > d_max) d_max = i;
|
|
|
|
|
}
|
|
|
|
|
nt_prev = nth_successor;
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(!found) {
|
|
|
|
|
FURI_LOG_E(
|
|
|
|
|
TAG,
|
|
|
|
|
"Failed to find distance for nt_enc %08lx",
|
|
|
|
|
nt_enc_temp_arr[collection_cycle]);
|
|
|
|
|
FURI_LOG_E(
|
|
|
|
|
TAG, "using key %06llx and uid %08lx, nt_prev is %08lx", known_key, cuid, nt_prev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dict_attack_ctx->d_median = get_median(d_all, d_all_cnt);
|
|
|
|
|
dict_attack_ctx->calibrated = true;
|
|
|
|
|
instance->state = MfClassicPollerStateNestedController;
|
|
|
|
|
|
|
|
|
|
mf_classic_poller_halt(instance);
|
|
|
|
|
FURI_LOG_E(
|
|
|
|
|
TAG,
|
|
|
|
|
"Calibration completed: med=%u min=%u max=%u static=%s",
|
|
|
|
|
dict_attack_ctx->d_median,
|
|
|
|
|
d_min,
|
|
|
|
|
d_max,
|
|
|
|
|
(d_min == d_max) ? "true" : "false");
|
|
|
|
|
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 09:10:02 -04:00
|
|
|
NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* instance) {
|
2024-08-09 20:22:19 -04:00
|
|
|
// TODO: Handle when nonce is not collected (retry counter? Do not increment nested_target_key)
|
|
|
|
|
NfcCommand command = NfcCommandReset;
|
2024-08-01 09:10:02 -04:00
|
|
|
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
2024-08-09 20:22:19 -04:00
|
|
|
bool collection_success = false;
|
2024-08-01 09:10:02 -04:00
|
|
|
|
|
|
|
|
do {
|
2024-08-03 21:35:52 -04:00
|
|
|
if(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) {
|
2024-08-04 23:53:21 -04:00
|
|
|
FURI_LOG_E(TAG, "Hard PRNG, skipping");
|
2024-08-01 09:10:02 -04:00
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
if(dict_attack_ctx->static_encrypted) {
|
|
|
|
|
FURI_LOG_E(TAG, "Static encrypted nonce detected");
|
|
|
|
|
if(dict_attack_ctx->backdoor == MfClassicBackdoorFM11RF08S) {
|
|
|
|
|
// TODO: Backdoor static nested attack with calibrated distance
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: If not present, just log nonces with parity bits, e.g.
|
|
|
|
|
/*
|
|
|
|
|
bool success = add_nested_nonce(
|
|
|
|
|
&result,
|
|
|
|
|
cuid,
|
|
|
|
|
dict_attack_ctx->reuse_key_sector,
|
|
|
|
|
nt_prev,
|
|
|
|
|
nt_enc_prev,
|
|
|
|
|
parity,
|
|
|
|
|
UINT16_MAX);
|
|
|
|
|
if(!success) {
|
|
|
|
|
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
uint8_t block =
|
|
|
|
|
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
|
2024-08-01 09:10:02 -04:00
|
|
|
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
|
2024-08-03 21:35:52 -04:00
|
|
|
|
2024-08-01 09:10:02 -04:00
|
|
|
MfClassicAuthContext auth_ctx = {};
|
|
|
|
|
MfClassicError error;
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
uint8_t nonce_pair_index = dict_attack_ctx->nested_target_key % 2;
|
|
|
|
|
uint8_t nt_enc_per_collection = 2 + nonce_pair_index;
|
|
|
|
|
MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ?
|
|
|
|
|
MfClassicKeyTypeA :
|
|
|
|
|
MfClassicKeyTypeB;
|
|
|
|
|
uint8_t target_block = (4 * (dict_attack_ctx->nested_target_key / 4)) + 3;
|
|
|
|
|
uint32_t nt_enc_temp_arr[nt_enc_per_collection];
|
|
|
|
|
uint8_t nt_enc_collected = 0;
|
2024-08-04 23:53:21 -04:00
|
|
|
uint8_t parity = 0;
|
2024-08-01 22:16:10 -04:00
|
|
|
|
2024-08-01 09:10:02 -04:00
|
|
|
// 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);
|
|
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
if(error != MfClassicErrorNone) {
|
2024-08-01 09:10:02 -04:00
|
|
|
FURI_LOG_E(TAG, "Failed to perform full authentication");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FURI_LOG_E(TAG, "Full authentication successful");
|
|
|
|
|
|
2024-08-04 23:53:21 -04:00
|
|
|
// Step 2: Perform nested authentication a variable number of times to get nt_enc at a different PRNG offset
|
|
|
|
|
// eg. Collect most commonly observed nonce from 3 auths to known sector and 4th to target, then separately the
|
|
|
|
|
// most commonly observed nonce from 4 auths to known sector and 5th to target. This gets us a nonce pair,
|
|
|
|
|
// at a known distance (confirmed by parity bits) telling us the nt_enc plain.
|
2024-08-09 20:22:19 -04:00
|
|
|
for(uint8_t collection_cycle = 0; collection_cycle < (nt_enc_per_collection - 1);
|
|
|
|
|
collection_cycle++) {
|
|
|
|
|
// This loop must match the calibrated loop
|
2024-08-01 22:16:10 -04:00
|
|
|
error = mf_classic_poller_auth_nested(
|
|
|
|
|
instance,
|
|
|
|
|
block,
|
|
|
|
|
&dict_attack_ctx->current_key,
|
|
|
|
|
dict_attack_ctx->current_key_type,
|
2024-08-09 20:22:19 -04:00
|
|
|
&auth_ctx,
|
|
|
|
|
false);
|
2024-08-01 09:10:02 -04:00
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
if(error != MfClassicErrorNone) {
|
2024-08-09 20:22:19 -04:00
|
|
|
FURI_LOG_E(TAG, "Failed to perform nested authentication %u", collection_cycle);
|
|
|
|
|
break;
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
nt_enc_temp_arr[collection_cycle] =
|
|
|
|
|
bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
|
|
|
|
|
nt_enc_collected++;
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
2024-08-09 20:22:19 -04:00
|
|
|
error = mf_classic_poller_auth_nested(
|
|
|
|
|
instance,
|
|
|
|
|
target_block,
|
|
|
|
|
&dict_attack_ctx->current_key,
|
|
|
|
|
target_key_type,
|
|
|
|
|
&auth_ctx,
|
|
|
|
|
true);
|
2024-08-01 09:10:02 -04:00
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
if(nt_enc_collected != (nt_enc_per_collection - 1)) {
|
|
|
|
|
FURI_LOG_E(TAG, "Failed to collect sufficient nt_enc values");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
|
|
|
|
|
// Collect parity bits
|
|
|
|
|
const uint8_t* parity_data = bit_buffer_get_parity(instance->rx_plain_buffer);
|
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
|
parity = (parity << 1) | (((parity_data[0] >> i) & 0x01) ^ 0x01);
|
|
|
|
|
}
|
|
|
|
|
// Decrypt the previous nonce
|
|
|
|
|
uint32_t nt_prev = nt_enc_temp_arr[nt_enc_collected - 1];
|
|
|
|
|
uint64_t known_key = bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, 6);
|
|
|
|
|
Crypto1 crypto_temp;
|
|
|
|
|
crypto1_init(&crypto_temp, known_key);
|
|
|
|
|
crypto1_word(&crypto_temp, nt_prev ^ cuid, 1);
|
|
|
|
|
uint32_t decrypted_nt_prev =
|
|
|
|
|
(nt_prev ^ lfsr_rollback_word(&crypto_temp, nt_prev ^ cuid, 1));
|
|
|
|
|
|
|
|
|
|
// Find matching nt_enc plain at expected distance
|
|
|
|
|
bool found_matching_nt = false;
|
|
|
|
|
uint32_t found_nt = 0;
|
|
|
|
|
uint16_t current_dist = 0;
|
|
|
|
|
const uint16_t max_dist = 16; // 32 would work too
|
|
|
|
|
// TODO: Better handling of overflows (allow wrap instead of stopping?)
|
|
|
|
|
while(!found_matching_nt && current_dist < max_dist &&
|
|
|
|
|
((dict_attack_ctx->d_median - current_dist) != 0) &&
|
|
|
|
|
((dict_attack_ctx->d_median + current_dist) != UINT16_MAX)) {
|
|
|
|
|
uint32_t nth_successor_positive =
|
|
|
|
|
prng_successor(decrypted_nt_prev, dict_attack_ctx->d_median + current_dist);
|
|
|
|
|
if(valid_nonce(
|
|
|
|
|
nth_successor_positive, nt_enc, nth_successor_positive ^ nt_enc, parity)) {
|
|
|
|
|
found_matching_nt = true;
|
|
|
|
|
found_nt = nth_successor_positive;
|
2024-08-01 09:10:02 -04:00
|
|
|
break;
|
2024-08-09 20:22:19 -04:00
|
|
|
}
|
|
|
|
|
if(current_dist > 0) {
|
|
|
|
|
uint32_t nth_successor_negative =
|
|
|
|
|
prng_successor(decrypted_nt_prev, dict_attack_ctx->d_median - current_dist);
|
|
|
|
|
if(valid_nonce(
|
|
|
|
|
nth_successor_negative, nt_enc, nth_successor_negative ^ nt_enc, parity)) {
|
|
|
|
|
found_matching_nt = true;
|
|
|
|
|
found_nt = nth_successor_negative;
|
|
|
|
|
break;
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
|
|
|
|
}
|
2024-08-09 20:22:19 -04:00
|
|
|
current_dist++;
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
// Add the nonce to the array
|
|
|
|
|
if(found_matching_nt) {
|
|
|
|
|
collection_success = add_nested_nonce(
|
|
|
|
|
&dict_attack_ctx->nested_nonce,
|
|
|
|
|
cuid,
|
|
|
|
|
dict_attack_ctx->nested_target_key,
|
|
|
|
|
found_nt,
|
|
|
|
|
nt_enc,
|
|
|
|
|
parity,
|
|
|
|
|
0);
|
|
|
|
|
if(!collection_success) {
|
|
|
|
|
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
FURI_LOG_E(TAG, "Failed to find matching nonce");
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
2024-08-09 20:22:19 -04:00
|
|
|
|
|
|
|
|
FURI_LOG_E(
|
|
|
|
|
TAG,
|
|
|
|
|
"Target: %u (nonce pair %u, key type %s, block %u)",
|
|
|
|
|
dict_attack_ctx->nested_target_key,
|
|
|
|
|
nonce_pair_index,
|
|
|
|
|
(target_key_type == MfClassicKeyTypeA) ? "A" : "B",
|
|
|
|
|
target_block);
|
|
|
|
|
FURI_LOG_E(TAG, "cuid: %08lx", cuid);
|
|
|
|
|
FURI_LOG_E(TAG, "nt_enc: %08lx", nt_enc);
|
|
|
|
|
FURI_LOG_E(
|
|
|
|
|
TAG,
|
|
|
|
|
"parity: %u%u%u%u",
|
|
|
|
|
((parity >> 3) & 1),
|
|
|
|
|
((parity >> 2) & 1),
|
|
|
|
|
((parity >> 1) & 1),
|
|
|
|
|
(parity & 1));
|
|
|
|
|
FURI_LOG_E(TAG, "nt_enc prev: %08lx", nt_prev);
|
|
|
|
|
FURI_LOG_E(TAG, "nt_enc prev decrypted: %08lx", decrypted_nt_prev);
|
2024-08-01 09:10:02 -04:00
|
|
|
} while(false);
|
|
|
|
|
|
2024-08-09 20:22:19 -04:00
|
|
|
// Probably belongs in the controller
|
|
|
|
|
if(collection_success) {
|
|
|
|
|
dict_attack_ctx->nested_target_key++;
|
|
|
|
|
dict_attack_ctx->retry_counter = 0;
|
|
|
|
|
} else {
|
|
|
|
|
dict_attack_ctx->retry_counter++;
|
|
|
|
|
}
|
2024-08-01 09:10:02 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedController;
|
|
|
|
|
|
|
|
|
|
mf_classic_poller_halt(instance);
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 07:04:43 -04:00
|
|
|
NfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instance) {
|
|
|
|
|
NfcCommand command = NfcCommandContinue;
|
2024-08-02 08:15:53 -04:00
|
|
|
//MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
2024-08-02 07:04:43 -04:00
|
|
|
// TODO: Nested dictionary attack with ks1
|
|
|
|
|
instance->state = MfClassicPollerStateNestedLog;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 09:10:02 -04:00
|
|
|
NfcCommand mf_classic_poller_handler_nested_log(MfClassicPoller* instance) {
|
2024-08-09 20:38:45 -04:00
|
|
|
// TODO: Fix this logging the same nonce twice, and there should only be 16 sectors (1K)
|
|
|
|
|
/*
|
|
|
|
|
uint8_t nonce_pair_index = dict_attack_ctx->nested_target_key % 2;
|
|
|
|
|
uint8_t nt_enc_per_collection = 2 + nonce_pair_index;
|
|
|
|
|
MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ?
|
|
|
|
|
MfClassicKeyTypeA :
|
|
|
|
|
MfClassicKeyTypeB;
|
|
|
|
|
uint8_t target_block = (4 * (dict_attack_ctx->nested_target_key / 4)) + 3;
|
|
|
|
|
*/
|
2024-08-01 22:16:10 -04:00
|
|
|
furi_assert(instance->mode_ctx.dict_attack_ctx.nested_nonce.count > 0);
|
|
|
|
|
furi_assert(instance->mode_ctx.dict_attack_ctx.nested_nonce.nonces);
|
|
|
|
|
|
2024-08-01 09:10:02 -04:00
|
|
|
NfcCommand command = NfcCommandContinue;
|
2024-08-01 22:16:10 -04:00
|
|
|
bool params_saved = false;
|
2024-08-01 09:10:02 -04:00
|
|
|
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
2024-08-01 22:16:10 -04:00
|
|
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
Stream* stream = buffered_file_stream_alloc(storage);
|
|
|
|
|
FuriString* temp_str = furi_string_alloc();
|
|
|
|
|
|
|
|
|
|
do {
|
2024-08-03 21:35:52 -04:00
|
|
|
if((dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) &&
|
|
|
|
|
(dict_attack_ctx->nested_nonce.count != 2)) {
|
|
|
|
|
FURI_LOG_E(
|
|
|
|
|
TAG,
|
|
|
|
|
"MfClassicPollerStateNestedLog expected 2 nonces, received %u",
|
|
|
|
|
dict_attack_ctx->nested_nonce.count);
|
2024-08-01 22:16:10 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-03 21:35:52 -04:00
|
|
|
if(!buffered_file_stream_open(
|
|
|
|
|
stream, MF_CLASSIC_NESTED_LOGS_FILE_PATH, FSAM_WRITE, FSOM_OPEN_APPEND))
|
|
|
|
|
break;
|
2024-08-01 22:16:10 -04:00
|
|
|
|
|
|
|
|
bool params_write_success = true;
|
|
|
|
|
for(size_t i = 0; i < dict_attack_ctx->nested_nonce.count; i++) {
|
|
|
|
|
MfClassicNestedNonce* nonce = &dict_attack_ctx->nested_nonce.nonces[i];
|
2024-08-03 21:35:52 -04:00
|
|
|
furi_string_printf(
|
|
|
|
|
temp_str,
|
|
|
|
|
"Sec %d key %c cuid %08lx",
|
|
|
|
|
(nonce->key_idx / 2),
|
|
|
|
|
(nonce->key_idx % 2 == 0) ? 'A' : 'B',
|
|
|
|
|
nonce->cuid);
|
|
|
|
|
for(uint8_t nt_idx = 0;
|
|
|
|
|
nt_idx < ((dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) ? 2 : 1);
|
|
|
|
|
nt_idx++) {
|
2024-08-01 22:16:10 -04:00
|
|
|
furi_string_cat_printf(
|
|
|
|
|
temp_str,
|
|
|
|
|
" nt%u %08lx ks%u %08lx par%u ",
|
|
|
|
|
nt_idx,
|
|
|
|
|
nonce->nt,
|
|
|
|
|
nt_idx,
|
|
|
|
|
nonce->nt_enc ^ nonce->nt,
|
|
|
|
|
nt_idx);
|
|
|
|
|
for(uint8_t pb = 0; pb < 4; pb++) {
|
|
|
|
|
furi_string_cat_printf(temp_str, "%u", (nonce->par >> (3 - pb)) & 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-03 21:35:52 -04:00
|
|
|
if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {
|
2024-08-01 22:16:10 -04:00
|
|
|
furi_string_cat_printf(temp_str, " dist %u\n", nonce->dist);
|
|
|
|
|
} else {
|
|
|
|
|
furi_string_cat_printf(temp_str, "\n");
|
|
|
|
|
}
|
|
|
|
|
if(!stream_write_string(stream, temp_str)) {
|
|
|
|
|
params_write_success = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!params_write_success) break;
|
|
|
|
|
|
|
|
|
|
params_saved = true;
|
|
|
|
|
} while(false);
|
|
|
|
|
|
|
|
|
|
furi_assert(params_saved);
|
2024-08-01 09:10:02 -04:00
|
|
|
free(dict_attack_ctx->nested_nonce.nonces);
|
2024-08-01 22:16:10 -04:00
|
|
|
dict_attack_ctx->nested_nonce.count = 0;
|
|
|
|
|
furi_string_free(temp_str);
|
|
|
|
|
buffered_file_stream_close(stream);
|
|
|
|
|
stream_free(stream);
|
|
|
|
|
furi_record_close(RECORD_STORAGE);
|
|
|
|
|
instance->state = MfClassicPollerStateNestedController;
|
2024-08-01 09:10:02 -04:00
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) {
|
|
|
|
|
// Iterate through keys
|
|
|
|
|
NfcCommand command = NfcCommandContinue;
|
|
|
|
|
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
2024-08-09 20:22:19 -04:00
|
|
|
if((dict_attack_ctx->nested_nonce.count > 0) &&
|
|
|
|
|
(dict_attack_ctx->nested_target_key % 2 == 0)) {
|
2024-08-03 21:35:52 -04:00
|
|
|
if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {
|
2024-08-02 07:04:43 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedDictAttack;
|
|
|
|
|
return command;
|
2024-08-03 21:35:52 -04:00
|
|
|
} else if(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) {
|
2024-08-02 07:04:43 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedLog;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
2024-08-01 09:10:02 -04:00
|
|
|
}
|
2024-08-03 21:35:52 -04:00
|
|
|
if(dict_attack_ctx->backdoor == MfClassicBackdoorUnknown) {
|
2024-08-01 09:10:02 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedAnalyzeBackdoor;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
2024-08-04 23:53:21 -04:00
|
|
|
// TODO: Need to think about how this works for Fudan backdoored tags.
|
|
|
|
|
// We could reset the .calibration field every sector to re-calibrate. Calibration function handles backdoor calibration too.
|
|
|
|
|
if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak && !dict_attack_ctx->calibrated) {
|
|
|
|
|
instance->state = MfClassicPollerStateNestedCalibrate;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
2024-08-09 20:22:19 -04:00
|
|
|
// Target all sectors, key A and B, first and second nonce
|
|
|
|
|
if(dict_attack_ctx->nested_target_key < (instance->sectors_total * 4)) {
|
|
|
|
|
if(dict_attack_ctx->retry_counter > 3) {
|
|
|
|
|
// Bad sector, skip
|
|
|
|
|
if(dict_attack_ctx->nested_nonce.nonces) {
|
|
|
|
|
free(dict_attack_ctx->nested_nonce.nonces);
|
|
|
|
|
dict_attack_ctx->nested_nonce.count = 0;
|
|
|
|
|
}
|
|
|
|
|
dict_attack_ctx->nested_target_key += 4;
|
|
|
|
|
dict_attack_ctx->retry_counter = 0;
|
|
|
|
|
}
|
2024-08-01 09:10:02 -04:00
|
|
|
instance->state = MfClassicPollerStateNestedCollectNtEnc;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
// TODO: If we've recovered all keys, read blocks and go to complete
|
|
|
|
|
instance->state = MfClassicPollerStateKeyReuseStart;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
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 07:08:09 +04:00
|
|
|
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,
|
2024-08-03 21:35:52 -04:00
|
|
|
[MfClassicPollerStateNestedAnalyzeBackdoor] =
|
|
|
|
|
mf_classic_poller_handler_nested_analyze_backdoor,
|
2024-08-04 23:53:21 -04:00
|
|
|
[MfClassicPollerStateNestedCalibrate] = mf_classic_poller_handler_nested_calibrate,
|
2024-08-03 21:35:52 -04:00
|
|
|
[MfClassicPollerStateNestedCollectNt] = mf_classic_poller_handler_nested_collect_nt,
|
|
|
|
|
[MfClassicPollerStateNestedController] = mf_classic_poller_handler_nested_controller,
|
|
|
|
|
[MfClassicPollerStateNestedCollectNtEnc] = mf_classic_poller_handler_nested_collect_nt_enc,
|
|
|
|
|
[MfClassicPollerStateNestedDictAttack] = mf_classic_poller_handler_nested_dict_attack,
|
2024-08-01 09:10:02 -04:00
|
|
|
[MfClassicPollerStateNestedLog] = mf_classic_poller_handler_nested_log,
|
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 07:08:09 +04:00
|
|
|
[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-03 21:35:52 -04:00
|
|
|
};
|