mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-31 02:23:04 -07:00
Merge branch 'feat/nfc-type-4-final' into mntm-dev
This commit is contained in:
@@ -8,11 +8,7 @@ App(
|
||||
stack_size=5 * 1024,
|
||||
order=30,
|
||||
resources="resources",
|
||||
sources=[
|
||||
"*.c*",
|
||||
"!plugins",
|
||||
"!nfc_cli.c",
|
||||
],
|
||||
sources=["*.c*", "!plugins", "!nfc_cli.c", "!cli"],
|
||||
fap_libs=["assets", "mbedtls"],
|
||||
fap_icon="icon.png",
|
||||
fap_category="NFC",
|
||||
@@ -573,5 +569,62 @@ App(
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="cli_nfc_ep",
|
||||
requires=["cli"],
|
||||
sources=["nfc_cli.c"],
|
||||
sources=[
|
||||
"helpers/mf_classic_key_cache.c",
|
||||
"helpers/protocol_support/iso14443_3a/iso14443_3a_render.c",
|
||||
"helpers/protocol_support/mf_ultralight/mf_ultralight_render.c",
|
||||
"cli/nfc_cli.c",
|
||||
"cli/nfc_cli_commands.c",
|
||||
"cli/nfc_cli_command_processor.c",
|
||||
"cli/commands/helpers/nfc_cli_format.c",
|
||||
"cli/commands/helpers/nfc_cli_scanner.c",
|
||||
"cli/commands/helpers/nfc_cli_protocol_parser.c",
|
||||
"cli/commands/raw/nfc_cli_command_raw.c",
|
||||
"cli/commands/raw/protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.c",
|
||||
"cli/commands/raw/protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.c",
|
||||
"cli/commands/raw/protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.c",
|
||||
"cli/commands/raw/protocol_handlers/felica/nfc_cli_raw_felica.c",
|
||||
"cli/commands/apdu/nfc_cli_command_apdu.c",
|
||||
"cli/commands/apdu/protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.c",
|
||||
"cli/commands/apdu/protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.c",
|
||||
"cli/commands/apdu/protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.c",
|
||||
"cli/commands/dump/nfc_cli_command_dump.c",
|
||||
"cli/commands/dump/protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.c",
|
||||
"cli/commands/dump/protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.c",
|
||||
"cli/commands/dump/protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.c",
|
||||
"cli/commands/dump/protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.c",
|
||||
"cli/commands/dump/protocols/iso15693_3/nfc_cli_dump_iso15693_3.c",
|
||||
"cli/commands/dump/protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.c",
|
||||
"cli/commands/dump/protocols/mf_classic/nfc_cli_dump_mf_classic.c",
|
||||
"cli/commands/dump/protocols/mf_plus/nfc_cli_dump_mf_plus.c",
|
||||
"cli/commands/dump/protocols/mf_desfire/nfc_cli_dump_mf_desfire.c",
|
||||
"cli/commands/dump/protocols/slix/nfc_cli_dump_slix.c",
|
||||
"cli/commands/dump/protocols/st25tb/nfc_cli_dump_st25tb.c",
|
||||
"cli/commands/dump/protocols/felica/nfc_cli_dump_felica.c",
|
||||
"cli/commands/mfu/nfc_cli_command_mfu.c",
|
||||
"cli/commands/mfu/nfc_cli_action_info.c",
|
||||
"cli/commands/mfu/nfc_cli_action_rdbl.c",
|
||||
"cli/commands/mfu/nfc_cli_action_wrbl.c",
|
||||
"cli/commands/nfc_cli_command_emulate.c",
|
||||
"cli/commands/nfc_cli_command_scanner.c",
|
||||
"cli/commands/nfc_cli_command_field.c",
|
||||
],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="banapass_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="banapass_plugin_ep",
|
||||
targets=["f7"],
|
||||
requires=["nfc"],
|
||||
sources=["plugins/supported_cards/banapass.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="aic_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="aic_plugin_ep",
|
||||
targets=["f7"],
|
||||
requires=["nfc"],
|
||||
sources=["plugins/supported_cards/aic.c"],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
#include "nfc_cli_command_apdu.h"
|
||||
#include "../helpers/nfc_cli_format.h"
|
||||
#include "../helpers/nfc_cli_protocol_parser.h"
|
||||
|
||||
#include "protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.h"
|
||||
#include "protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.h"
|
||||
#include "protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <nfc/nfc.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#include <toolbox/args.h>
|
||||
#include <m-array.h>
|
||||
#include <m-algo.h>
|
||||
|
||||
#define TAG "APDU"
|
||||
|
||||
#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)
|
||||
|
||||
typedef NfcCommand (
|
||||
*NfcCliApduProtocolHandler)(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
|
||||
static const char* raw_error_names[] = {
|
||||
[NfcCliApduErrorNone] = "None",
|
||||
[NfcCliApduErrorTimeout] = "Timeout",
|
||||
[NfcCliApduErrorProtocol] = "Internal protocol",
|
||||
[NfcCliApduErrorWrongCrc] = "Wrong CRC",
|
||||
[NfcCliApduErrorNotPresent] = "No card",
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NfcCliProtocolRequestTypeNormalExecute,
|
||||
NfcCliProtocolRequestTypeAbort,
|
||||
} NfcCliProtocolRequestType;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* data;
|
||||
size_t size;
|
||||
} NfcCliApduData;
|
||||
|
||||
static void ApduItem_init(NfcCliApduData* item) {
|
||||
item->size = 0;
|
||||
item->data = NULL;
|
||||
}
|
||||
|
||||
static void ApduItem_init_set(NfcCliApduData* item, const NfcCliApduData* src) {
|
||||
item->data = malloc(src->size);
|
||||
item->size = src->size;
|
||||
memcpy(item->data, src->data, src->size);
|
||||
}
|
||||
|
||||
static void ApduItem_set(NfcCliApduData* item, const NfcCliApduData* src) {
|
||||
if(item->data == NULL) {
|
||||
item->data = malloc(src->size);
|
||||
} else if(item->size != src->size) {
|
||||
uint8_t* buf = realloc(item->data, src->size);
|
||||
furi_check(buf);
|
||||
item->data = buf;
|
||||
}
|
||||
|
||||
item->size = src->size;
|
||||
memcpy(item->data, src->data, src->size);
|
||||
}
|
||||
|
||||
static void ApduItem_clear(NfcCliApduData* item) {
|
||||
if(item->data) free(item->data);
|
||||
item->data = NULL;
|
||||
item->size = 0;
|
||||
}
|
||||
|
||||
ARRAY_DEF(
|
||||
NfcCliApduItemArray,
|
||||
NfcCliApduData,
|
||||
(INIT(API_2(ApduItem_init)),
|
||||
SET(API_6(ApduItem_set)),
|
||||
INIT_SET(API_6(ApduItem_init_set)),
|
||||
CLEAR(API_2(ApduItem_clear))))
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
bool auto_detect;
|
||||
NfcCliApduItemArray_t apdu;
|
||||
NfcCliApduRequestResponse data;
|
||||
FuriSemaphore* sem_done;
|
||||
FuriMessageQueue* input_queue;
|
||||
} NfcCliApduContext;
|
||||
|
||||
static NfcCliActionContext* nfc_cli_apdu_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliApduContext* instance = malloc(sizeof(NfcCliApduContext));
|
||||
instance->nfc = nfc;
|
||||
instance->data.protocol = NfcProtocolInvalid;
|
||||
instance->auto_detect = true;
|
||||
NfcCliApduItemArray_init(instance->apdu);
|
||||
instance->data.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
|
||||
instance->data.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
|
||||
|
||||
instance->sem_done = furi_semaphore_alloc(1, 0);
|
||||
instance->input_queue = furi_message_queue_alloc(1, sizeof(NfcCliProtocolRequestType));
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void nfc_cli_apdu_free_ctx(NfcCliActionContext* action_ctx) {
|
||||
furi_assert(action_ctx);
|
||||
NfcCliApduContext* instance = action_ctx;
|
||||
|
||||
instance->nfc = NULL;
|
||||
NfcCliApduItemArray_clear(instance->apdu);
|
||||
|
||||
bit_buffer_free(instance->data.rx_buffer);
|
||||
bit_buffer_free(instance->data.tx_buffer);
|
||||
furi_semaphore_free(instance->sem_done);
|
||||
furi_message_queue_free(instance->input_queue);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static inline void nfc_cli_apdu_print_result(const NfcCliApduContext* instance) {
|
||||
nfc_cli_printf_array(
|
||||
bit_buffer_get_data(instance->data.tx_buffer),
|
||||
bit_buffer_get_size_bytes(instance->data.tx_buffer),
|
||||
"\r\nTx: ");
|
||||
|
||||
if(instance->data.result != NfcCliApduErrorNone)
|
||||
printf("\r\nError: \"%s\"\r\n", raw_error_names[instance->data.result]);
|
||||
|
||||
size_t rx_size = bit_buffer_get_size_bytes(instance->data.rx_buffer);
|
||||
if(rx_size > 0) {
|
||||
nfc_cli_printf_array(
|
||||
bit_buffer_get_data(instance->data.rx_buffer),
|
||||
bit_buffer_get_size_bytes(instance->data.rx_buffer),
|
||||
"\r\nRx: ");
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static NfcProtocol nfc_cli_apdu_protocol_autodetect(Nfc* nfc) {
|
||||
const NfcProtocol supported_protocols[] = {
|
||||
NfcProtocolIso14443_4a,
|
||||
NfcProtocolIso14443_4b,
|
||||
NfcProtocolIso15693_3,
|
||||
};
|
||||
|
||||
const char* supported_names[] = {"Iso14443_4a", "Iso14443_4b", "Iso15693_3"};
|
||||
|
||||
NfcProtocol protocol = NfcProtocolInvalid;
|
||||
for(uint8_t i = 0; i < COUNT_OF(supported_protocols); i++) {
|
||||
NfcPoller* poller = nfc_poller_alloc(nfc, supported_protocols[i]);
|
||||
bool is_detected = nfc_poller_detect(poller);
|
||||
nfc_poller_free(poller);
|
||||
if(is_detected) {
|
||||
protocol = supported_protocols[i];
|
||||
printf("Detected tag: %s\r\n", supported_names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return protocol;
|
||||
}
|
||||
|
||||
static NfcCliApduProtocolHandler nfc_cli_apdu_poller_get_handler(NfcProtocol protocol) {
|
||||
if(protocol == NfcProtocolIso14443_4a)
|
||||
return nfc_cli_apdu_iso14443_4a_handler;
|
||||
else if(protocol == NfcProtocolIso14443_4b)
|
||||
return nfc_cli_apdu_iso14443_4b_handler;
|
||||
else if(protocol == NfcProtocolIso15693_3)
|
||||
return nfc_cli_apdu_iso15693_3_handler;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static NfcCommand nfc_cli_apdu_poller_callback(NfcGenericEvent event, void* context) {
|
||||
NfcCliApduContext* instance = context;
|
||||
|
||||
FURI_LOG_D(TAG, "Poller callback");
|
||||
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;
|
||||
furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);
|
||||
|
||||
NfcCommand command = NfcCommandStop;
|
||||
if(request_type == NfcCliProtocolRequestTypeAbort) {
|
||||
FURI_LOG_D(TAG, "Aborting poller callback");
|
||||
} else {
|
||||
NfcCliApduProtocolHandler handler =
|
||||
nfc_cli_apdu_poller_get_handler(instance->data.protocol);
|
||||
if(handler) command = handler(event, &instance->data);
|
||||
}
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_cli_apdu_execute(PipeSide* pipe, void* context) {
|
||||
UNUSED(pipe);
|
||||
furi_assert(context);
|
||||
NfcCliApduContext* instance = context;
|
||||
|
||||
if(instance->auto_detect) {
|
||||
instance->data.protocol = nfc_cli_apdu_protocol_autodetect(instance->nfc);
|
||||
}
|
||||
|
||||
if(instance->data.protocol != NfcProtocolInvalid) {
|
||||
NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->data.protocol);
|
||||
|
||||
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeNormalExecute;
|
||||
nfc_poller_start(poller, nfc_cli_apdu_poller_callback, instance);
|
||||
|
||||
NfcCliApduItemArray_it_t it;
|
||||
for(NfcCliApduItemArray_it(it, instance->apdu); !NfcCliApduItemArray_end_p(it);
|
||||
NfcCliApduItemArray_next(it)) {
|
||||
const NfcCliApduData* item = NfcCliApduItemArray_cref(it);
|
||||
bit_buffer_copy_bytes(instance->data.tx_buffer, item->data, item->size);
|
||||
bit_buffer_reset(instance->data.rx_buffer);
|
||||
|
||||
furi_message_queue_put(instance->input_queue, &request_type, FuriWaitForever);
|
||||
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
|
||||
nfc_cli_apdu_print_result(instance);
|
||||
if(instance->data.result != NfcCliApduErrorNone) break;
|
||||
}
|
||||
|
||||
request_type = NfcCliProtocolRequestTypeAbort;
|
||||
furi_message_queue_put(instance->input_queue, &request_type, FuriWaitForever);
|
||||
nfc_poller_stop(poller);
|
||||
nfc_poller_free(poller);
|
||||
}
|
||||
}
|
||||
|
||||
static const NfcProtocolNameValuePair supported_protocols[] = {
|
||||
{.name = "4a", .value = NfcProtocolIso14443_4a},
|
||||
{.name = "4b", .value = NfcProtocolIso14443_4b},
|
||||
{.name = "15", .value = NfcProtocolIso15693_3},
|
||||
};
|
||||
|
||||
static bool nfc_cli_apdu_parse_protocol(FuriString* value, void* output) {
|
||||
NfcCliApduContext* ctx = output;
|
||||
ctx->auto_detect = false;
|
||||
|
||||
NfcCliProtocolParser* parser =
|
||||
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
|
||||
|
||||
bool result = nfc_cli_protocol_parser_get(parser, value, &ctx->data.protocol);
|
||||
|
||||
nfc_cli_protocol_parser_free(parser);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool nfc_cli_apdu_parse_data(FuriString* value, void* output) {
|
||||
NfcCliApduContext* ctx = output;
|
||||
|
||||
bool result = false;
|
||||
FuriString* word = furi_string_alloc();
|
||||
|
||||
while(args_read_string_and_trim(value, word)) {
|
||||
size_t len = furi_string_size(word);
|
||||
if(len % 2 != 0) break;
|
||||
|
||||
size_t data_length = len / 2;
|
||||
|
||||
const size_t max_len = UINT16_MAX;
|
||||
if(data_length > max_len) {
|
||||
printf(
|
||||
ANSI_FG_RED "\r\nData payload is too long, max length = %d bytes\r\n" ANSI_RESET,
|
||||
max_len);
|
||||
break;
|
||||
}
|
||||
|
||||
NfcCliApduData* item = NfcCliApduItemArray_push_new(ctx->apdu);
|
||||
item->size = data_length;
|
||||
item->data = malloc(data_length);
|
||||
result = args_read_hex_bytes(word, item->data, item->size);
|
||||
}
|
||||
furi_string_free(word);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const NfcCliKeyDescriptor apdu_keys[] = {
|
||||
{
|
||||
.long_name = "protocol",
|
||||
.short_name = "p",
|
||||
.description = "set protocol (4a, 4b, 15) directly, otherwise autodetected",
|
||||
.features = {.parameter = true, .required = false},
|
||||
.parse = nfc_cli_apdu_parse_protocol,
|
||||
},
|
||||
{
|
||||
.long_name = "data",
|
||||
.short_name = "d",
|
||||
.description = "apdu payloads in format p1 p2 p3",
|
||||
.features = {.parameter = true, .multivalue = true, .required = true},
|
||||
.parse = nfc_cli_apdu_parse_data,
|
||||
},
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor apdu_action = {
|
||||
.name = "apdu",
|
||||
.description = "Send APDU data to iso14443_4a, iso14443_4b or iso15693_3",
|
||||
.alloc = nfc_cli_apdu_alloc_ctx,
|
||||
.free = nfc_cli_apdu_free_ctx,
|
||||
.execute = nfc_cli_apdu_execute,
|
||||
.key_count = COUNT_OF(apdu_keys),
|
||||
.keys = apdu_keys,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor* apdu_actions_collection[] = {&apdu_action};
|
||||
|
||||
//Command descriptor
|
||||
ADD_NFC_CLI_COMMAND(apdu, "", apdu_actions_collection);
|
||||
|
||||
//Command usage: apdu <protocol> <data>
|
||||
//Command examples:
|
||||
//apdu -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
|
||||
//apdu -p 4a -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
|
||||
//apdu -p 4b -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
|
||||
//apdu -p 15 -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor apdu_cmd;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#include "nfc_cli_apdu_iso14443_4a.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/protocols/iso14443_4a/iso14443_4a.h>
|
||||
#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||
|
||||
#define TAG "ISO14A_4A"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static NfcCliApduError nfc_cli_apdu_iso14443_4a_process_error(Iso14443_4aError error) {
|
||||
switch(error) {
|
||||
case Iso14443_4aErrorNone:
|
||||
return NfcCliApduErrorNone;
|
||||
case Iso14443_4aErrorTimeout:
|
||||
return NfcCliApduErrorTimeout;
|
||||
case Iso14443_4aErrorNotPresent:
|
||||
return NfcCliApduErrorNotPresent;
|
||||
default:
|
||||
return NfcCliApduErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {
|
||||
Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
Iso14443_4aError err = iso14443_4a_poller_send_block(
|
||||
event.instance, instance->tx_buffer, instance->rx_buffer);
|
||||
instance->result = nfc_cli_apdu_iso14443_4a_process_error(err);
|
||||
if(err != Iso14443_4aErrorNone) command = NfcCommandStop;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_apdu_common_types.h"
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#include "nfc_cli_apdu_iso14443_4b.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/protocols/iso14443_4b/iso14443_4b.h>
|
||||
#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
|
||||
|
||||
#define TAG "ISO14A_4B"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static NfcCliApduError nfc_cli_apdu_iso14443_4b_process_error(Iso14443_4bError error) {
|
||||
switch(error) {
|
||||
case Iso14443_4bErrorNone:
|
||||
return NfcCliApduErrorNone;
|
||||
case Iso14443_4bErrorTimeout:
|
||||
return NfcCliApduErrorTimeout;
|
||||
case Iso14443_4bErrorNotPresent:
|
||||
return NfcCliApduErrorNotPresent;
|
||||
default:
|
||||
return NfcCliApduErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {
|
||||
Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
|
||||
Iso14443_4bError err = iso14443_4b_poller_send_block(
|
||||
event.instance, instance->tx_buffer, instance->rx_buffer);
|
||||
instance->result = nfc_cli_apdu_iso14443_4b_process_error(err);
|
||||
if(err != Iso14443_4bErrorNone) command = NfcCommandStop;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_apdu_common_types.h"
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#include "nfc_cli_apdu_iso15693_3.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/protocols/iso15693_3/iso15693_3.h>
|
||||
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
|
||||
|
||||
#define TAG "ISO15"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static NfcCliApduError nfc_cli_apdu_iso15693_3_process_error(Iso15693_3Error error) {
|
||||
switch(error) {
|
||||
case Iso15693_3ErrorNone:
|
||||
return NfcCliApduErrorNone;
|
||||
case Iso15693_3ErrorTimeout:
|
||||
return NfcCliApduErrorTimeout;
|
||||
case Iso15693_3ErrorNotPresent:
|
||||
return NfcCliApduErrorNotPresent;
|
||||
default:
|
||||
return NfcCliApduErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {
|
||||
Iso15693_3PollerEvent* iso15693_3_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {
|
||||
Iso15693_3Error err = iso15693_3_poller_send_frame(
|
||||
event.instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
|
||||
instance->result = nfc_cli_apdu_iso15693_3_process_error(err);
|
||||
if(err != Iso15693_3ErrorNone) command = NfcCommandStop;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_apdu_common_types.h"
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <nfc/nfc.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
typedef enum {
|
||||
NfcCliApduErrorNone,
|
||||
NfcCliApduErrorTimeout,
|
||||
NfcCliApduErrorNotPresent,
|
||||
NfcCliApduErrorWrongCrc,
|
||||
NfcCliApduErrorProtocol,
|
||||
} NfcCliApduError;
|
||||
|
||||
typedef struct {
|
||||
NfcProtocol protocol;
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
NfcCliApduError result;
|
||||
} NfcCliApduRequestResponse;
|
||||
@@ -0,0 +1,319 @@
|
||||
#include "nfc_cli_command_dump.h"
|
||||
#include "protocols/nfc_cli_dump_common_types.h"
|
||||
#include "../helpers/nfc_cli_format.h"
|
||||
#include "../helpers/nfc_cli_protocol_parser.h"
|
||||
#include "../helpers/nfc_cli_scanner.h"
|
||||
|
||||
#include "protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.h"
|
||||
#include "protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.h"
|
||||
#include "protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.h"
|
||||
#include "protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.h"
|
||||
#include "protocols/iso15693_3/nfc_cli_dump_iso15693_3.h"
|
||||
#include "protocols/mf_classic/nfc_cli_dump_mf_classic.h"
|
||||
#include "protocols/mf_desfire/nfc_cli_dump_mf_desfire.h"
|
||||
#include "protocols/mf_plus/nfc_cli_dump_mf_plus.h"
|
||||
#include "protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.h"
|
||||
#include "protocols/slix/nfc_cli_dump_slix.h"
|
||||
#include "protocols/st25tb/nfc_cli_dump_st25tb.h"
|
||||
#include "protocols/felica/nfc_cli_dump_felica.h"
|
||||
|
||||
#include <datetime.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
#include <toolbox/strint.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#define NFC_DEFAULT_FOLDER EXT_PATH("nfc")
|
||||
#define NFC_FILE_EXTENSION ".nfc"
|
||||
#define NFC_CLI_DEFAULT_FILENAME_PREFIX "dump"
|
||||
|
||||
#define NFC_CLI_DUMP_DEFAULT_TIMEOUT (5000)
|
||||
|
||||
#define TAG "DUMP"
|
||||
|
||||
static const char* nfc_cli_dump_error_names[NfcCliDumpErrorNum] = {
|
||||
[NfcCliDumpErrorNone] = "",
|
||||
[NfcCliDumpErrorNotPresent] = "card not present",
|
||||
[NfcCliDumpErrorAuthFailed] = "authentication failed",
|
||||
[NfcCliDumpErrorTimeout] = "timeout",
|
||||
[NfcCliDumpErrorFailedToRead] = "failed to read",
|
||||
};
|
||||
|
||||
static NfcCliActionContext* nfc_cli_dump_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliDumpContext* instance = malloc(sizeof(NfcCliDumpContext));
|
||||
instance->nfc = nfc;
|
||||
instance->file_path = furi_string_alloc();
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->sem_done = furi_semaphore_alloc(1, 0);
|
||||
instance->nfc_device = nfc_device_alloc();
|
||||
instance->desired_protocol = NfcProtocolInvalid;
|
||||
instance->auth_ctx.skip_auth = true;
|
||||
instance->auth_ctx.key_size = 0;
|
||||
instance->timeout = NFC_CLI_DUMP_DEFAULT_TIMEOUT;
|
||||
|
||||
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
||||
instance->scanner = nfc_cli_scanner_alloc(nfc);
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void nfc_cli_dump_free_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliDumpContext* instance = ctx;
|
||||
instance->desired_protocol = NfcProtocolInvalid;
|
||||
furi_string_free(instance->file_path);
|
||||
instance->nfc = NULL;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_semaphore_free(instance->sem_done);
|
||||
nfc_device_free(instance->nfc_device);
|
||||
|
||||
mf_classic_key_cache_free(instance->mfc_key_cache);
|
||||
nfc_cli_scanner_free(instance->scanner);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool nfc_cli_dump_parse_filename_key(FuriString* value, void* output) {
|
||||
furi_assert(value);
|
||||
furi_assert(output);
|
||||
NfcCliDumpContext* ctx = output;
|
||||
furi_string_set(ctx->file_path, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
NfcGenericCallback protocol_poller_callbacks[NfcProtocolNum] = {
|
||||
[NfcProtocolMfUltralight] = nfc_cli_dump_poller_callback_mf_ultralight,
|
||||
[NfcProtocolMfClassic] = nfc_cli_dump_poller_callback_mf_classic,
|
||||
[NfcProtocolFelica] = nfc_cli_dump_poller_callback_felica,
|
||||
[NfcProtocolIso14443_3a] = nfc_cli_dump_poller_callback_iso14443_3a,
|
||||
[NfcProtocolIso14443_3b] = nfc_cli_dump_poller_callback_iso14443_3b,
|
||||
[NfcProtocolIso14443_4a] = nfc_cli_dump_poller_callback_iso14443_4a,
|
||||
[NfcProtocolIso14443_4b] = nfc_cli_dump_poller_callback_iso14443_4b,
|
||||
[NfcProtocolIso15693_3] = nfc_cli_dump_poller_callback_iso15693_3,
|
||||
[NfcProtocolSlix] = nfc_cli_dump_poller_callback_slix,
|
||||
[NfcProtocolMfDesfire] = nfc_cli_dump_poller_callback_mf_desfire,
|
||||
[NfcProtocolMfPlus] = nfc_cli_dump_poller_callback_mf_plus,
|
||||
[NfcProtocolSt25tb] = nfc_cli_dump_poller_callback_st25tb,
|
||||
};
|
||||
|
||||
static void nfc_cli_dump_generate_filename(FuriString* file_path) {
|
||||
furi_string_set_str(file_path, NFC_DEFAULT_FOLDER);
|
||||
DateTime dt;
|
||||
furi_hal_rtc_get_datetime(&dt);
|
||||
furi_string_cat_printf(
|
||||
file_path,
|
||||
"/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%s",
|
||||
NFC_CLI_DEFAULT_FILENAME_PREFIX,
|
||||
dt.year,
|
||||
dt.month,
|
||||
dt.day,
|
||||
dt.hour,
|
||||
dt.minute,
|
||||
dt.second,
|
||||
NFC_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
static bool nfc_cli_dump_check_filepath_valid(FuriString* file_path, Storage* storage) {
|
||||
bool file_exists = false;
|
||||
bool dir_exists = false;
|
||||
|
||||
FuriString* buf = furi_string_alloc();
|
||||
|
||||
path_extract_dirname(furi_string_get_cstr(file_path), buf);
|
||||
dir_exists = storage_dir_exists(storage, furi_string_get_cstr(buf));
|
||||
file_exists = storage_file_exists(storage, furi_string_get_cstr(file_path));
|
||||
|
||||
bool result = true;
|
||||
if(!dir_exists) {
|
||||
printf(ANSI_FG_RED "Path \'%s\' doesn't exist\r\n" ANSI_RESET, furi_string_get_cstr(buf));
|
||||
result = false;
|
||||
} else if(file_exists) {
|
||||
printf(
|
||||
ANSI_FG_RED "File \'%s\' already exists\r\n" ANSI_RESET,
|
||||
furi_string_get_cstr(file_path));
|
||||
result = false;
|
||||
}
|
||||
furi_string_free(buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool nfc_cli_dump_process_filename(NfcCliDumpContext* instance) {
|
||||
bool result = false;
|
||||
if(furi_string_empty(instance->file_path)) {
|
||||
nfc_cli_dump_generate_filename(instance->file_path);
|
||||
result = true;
|
||||
} else {
|
||||
result = nfc_cli_dump_check_filepath_valid(instance->file_path, instance->storage);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t nfc_cli_dump_set_protocol(NfcCliDumpContext* instance) {
|
||||
size_t protocol_count = 0;
|
||||
if(instance->desired_protocol != NfcProtocolInvalid) {
|
||||
protocol_count = 1;
|
||||
} else {
|
||||
if(!nfc_cli_scanner_detect_protocol(instance->scanner, instance->timeout)) {
|
||||
NfcCliDumpError error = NfcCliDumpErrorTimeout;
|
||||
printf(ANSI_FG_RED "Error: %s\r\n" ANSI_RESET, nfc_cli_dump_error_names[error]);
|
||||
} else {
|
||||
nfc_cli_scanner_list_detected_protocols(instance->scanner);
|
||||
protocol_count = nfc_cli_scanner_detected_protocol_num(instance->scanner);
|
||||
instance->desired_protocol = nfc_cli_scanner_get_protocol(instance->scanner, 0);
|
||||
}
|
||||
}
|
||||
return protocol_count;
|
||||
}
|
||||
|
||||
static bool nfc_cli_dump_card(NfcCliDumpContext* instance) {
|
||||
instance->poller = nfc_poller_alloc(instance->nfc, instance->desired_protocol);
|
||||
NfcGenericCallback callback = protocol_poller_callbacks[instance->desired_protocol];
|
||||
if(callback) {
|
||||
nfc_poller_start(instance->poller, callback, instance);
|
||||
FuriStatus status = furi_semaphore_acquire(instance->sem_done, instance->timeout);
|
||||
|
||||
if(status == FuriStatusErrorTimeout) instance->result = NfcCliDumpErrorTimeout;
|
||||
nfc_poller_stop(instance->poller);
|
||||
}
|
||||
nfc_poller_free(instance->poller);
|
||||
|
||||
return instance->result == NfcCliDumpErrorNone;
|
||||
}
|
||||
|
||||
static void nfc_cli_dump_execute(PipeSide* pipe, NfcCliActionContext* context) {
|
||||
UNUSED(pipe);
|
||||
furi_assert(context);
|
||||
NfcCliDumpContext* instance = context;
|
||||
do {
|
||||
if(!nfc_cli_dump_process_filename(instance)) break;
|
||||
|
||||
size_t protocol_count = nfc_cli_dump_set_protocol(instance);
|
||||
if(instance->desired_protocol == NfcProtocolInvalid) break;
|
||||
|
||||
printf("Dumping as \"%s\"\r\n", nfc_cli_get_protocol_name(instance->desired_protocol));
|
||||
if(protocol_count > 1) printf("Use \'-p\' key to specify another protocol\r\n");
|
||||
|
||||
if(nfc_cli_dump_card(instance)) {
|
||||
const char* path = furi_string_get_cstr(instance->file_path);
|
||||
if(nfc_device_save(instance->nfc_device, path)) {
|
||||
printf("Dump saved to \'%s\'\r\n", path);
|
||||
}
|
||||
} else {
|
||||
printf(
|
||||
ANSI_FG_RED "Error: %s\r\n" ANSI_RESET,
|
||||
nfc_cli_dump_error_names[instance->result]);
|
||||
}
|
||||
} while(false);
|
||||
}
|
||||
|
||||
static const NfcProtocolNameValuePair supported_protocols[] = {
|
||||
{.name = "14_3a", .value = NfcProtocolIso14443_3a},
|
||||
{.name = "14_3b", .value = NfcProtocolIso14443_3b},
|
||||
{.name = "14_4a", .value = NfcProtocolIso14443_4a},
|
||||
{.name = "14_4b", .value = NfcProtocolIso14443_4b},
|
||||
{.name = "15", .value = NfcProtocolIso15693_3},
|
||||
{.name = "felica", .value = NfcProtocolFelica},
|
||||
{.name = "mfu", .value = NfcProtocolMfUltralight},
|
||||
{.name = "mfc", .value = NfcProtocolMfClassic},
|
||||
{.name = "mfp", .value = NfcProtocolMfPlus},
|
||||
{.name = "des", .value = NfcProtocolMfDesfire},
|
||||
{.name = "slix", .value = NfcProtocolSlix},
|
||||
{.name = "st25", .value = NfcProtocolSt25tb},
|
||||
};
|
||||
|
||||
static bool nfc_cli_dump_parse_protocol(FuriString* value, void* output) {
|
||||
furi_assert(value);
|
||||
furi_assert(output);
|
||||
NfcCliDumpContext* ctx = output;
|
||||
|
||||
NfcCliProtocolParser* parser =
|
||||
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
|
||||
|
||||
bool result = nfc_cli_protocol_parser_get(parser, value, &ctx->desired_protocol);
|
||||
|
||||
nfc_cli_protocol_parser_free(parser);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool nfc_cli_dump_parse_key(FuriString* value, void* output) {
|
||||
furi_assert(value);
|
||||
furi_assert(output);
|
||||
NfcCliDumpContext* ctx = output;
|
||||
NfcCliDumpAuthContext* auth_ctx = &ctx->auth_ctx;
|
||||
|
||||
bool result = false;
|
||||
do {
|
||||
size_t len = furi_string_size(value);
|
||||
if(len % 2 != 0) break;
|
||||
size_t data_length = len / 2;
|
||||
|
||||
if(data_length != MF_ULTRALIGHT_AUTH_PASSWORD_SIZE &&
|
||||
data_length != MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE) {
|
||||
printf(ANSI_FG_RED "Error: Wrong key size" ANSI_RESET);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!args_read_hex_bytes(value, auth_ctx->key.key, data_length)) break;
|
||||
auth_ctx->key_size = data_length;
|
||||
auth_ctx->skip_auth = false;
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool nfc_cli_dump_parse_timeout(FuriString* value, NfcCliActionContext* output) {
|
||||
NfcCliDumpContext* ctx = output;
|
||||
|
||||
StrintParseError err = strint_to_uint32(furi_string_get_cstr(value), NULL, &ctx->timeout, 10);
|
||||
return err == StrintParseNoError;
|
||||
}
|
||||
|
||||
const NfcCliKeyDescriptor dump_keys[] = {
|
||||
{
|
||||
.long_name = "key",
|
||||
.short_name = "k",
|
||||
.description = "key to path auth in protocols which requires it",
|
||||
.features = {.required = false, .parameter = true},
|
||||
.parse = nfc_cli_dump_parse_key,
|
||||
},
|
||||
{
|
||||
.long_name = "protocol",
|
||||
.short_name = "p",
|
||||
.description = "desired protocol",
|
||||
.features = {.required = false, .parameter = true},
|
||||
.parse = nfc_cli_dump_parse_protocol,
|
||||
},
|
||||
{
|
||||
.features = {.required = false, .parameter = true},
|
||||
.long_name = "file",
|
||||
.short_name = "f",
|
||||
.description = "path to new file",
|
||||
.parse = nfc_cli_dump_parse_filename_key,
|
||||
},
|
||||
{
|
||||
.features = {.required = false, .parameter = true},
|
||||
.long_name = "timeout",
|
||||
.short_name = "t",
|
||||
.description = "timeout value in milliseconds",
|
||||
.parse = nfc_cli_dump_parse_timeout,
|
||||
},
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor dump_action = {
|
||||
.name = "dump",
|
||||
.description = "Dump tag to .nfc file",
|
||||
.alloc = nfc_cli_dump_alloc_ctx,
|
||||
.free = nfc_cli_dump_free_ctx,
|
||||
.execute = nfc_cli_dump_execute,
|
||||
.key_count = COUNT_OF(dump_keys),
|
||||
.keys = dump_keys,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor* dump_actions_collection[] = {&dump_action};
|
||||
|
||||
//Command descriptor
|
||||
ADD_NFC_CLI_COMMAND(dump, "", dump_actions_collection);
|
||||
|
||||
//Command examples:
|
||||
//dump -f ext/nfc/test.nfc
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor dump_cmd;
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "nfc_cli_dump_felica.h"
|
||||
#include <nfc/protocols/felica/felica_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolFelica);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const FelicaPollerEvent* felica_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(felica_event->type == FelicaPollerEventTypeReady ||
|
||||
felica_event->type == FelicaPollerEventTypeIncomplete) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||
command = NfcCommandStop;
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
} else if(felica_event->type == FelicaPollerEventTypeError) {
|
||||
command = NfcCommandStop;
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
|
||||
FelicaAuthenticationContext* ctx = felica_event->data->auth_context;
|
||||
const NfcCliDumpAuthContext* dump_auth_ctx = &instance->auth_ctx;
|
||||
ctx->skip_auth = dump_auth_ctx->skip_auth;
|
||||
ctx->card_key = dump_auth_ctx->key.felica_key;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context);
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#include "nfc_cli_dump_iso14443_3a.h"
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_3a);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller));
|
||||
command = NfcCommandStop;
|
||||
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
|
||||
command = NfcCommandStop;
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_3a(NfcGenericEvent event, void* context);
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#include "nfc_cli_dump_iso14443_3b.h"
|
||||
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_3b);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller));
|
||||
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
command = NfcCommandStop;
|
||||
} else if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeError) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_3b(NfcGenericEvent event, void* context);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#include "nfc_cli_dump_iso14443_4a.h"
|
||||
#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(instance->poller));
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
command = NfcCommandStop;
|
||||
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_4a(NfcGenericEvent event, void* context);
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#include "nfc_cli_dump_iso14443_4b.h"
|
||||
#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_4b(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_4b);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(instance->poller));
|
||||
command = NfcCommandStop;
|
||||
} else if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeError) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso14443_4b(NfcGenericEvent event, void* context);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#include "nfc_cli_dump_iso15693_3.h"
|
||||
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso15693_3(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso15693_3);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const Iso15693_3PollerEvent* iso15693_3_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolIso15693_3, nfc_poller_get_data(instance->poller));
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
} else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_iso15693_3(NfcGenericEvent event, void* context);
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#include "nfc_cli_dump_mf_classic.h"
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
|
||||
|
||||
#define TAG "MFC"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_classic(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfClassic);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const MfClassicPollerEvent* mfc_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));
|
||||
size_t uid_len = 0;
|
||||
const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);
|
||||
if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) {
|
||||
FURI_LOG_D(TAG, "Key cache found");
|
||||
mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Key cache not found");
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {
|
||||
uint8_t sector_num = 0;
|
||||
MfClassicKey key = {};
|
||||
MfClassicKeyType key_type = MfClassicKeyTypeA;
|
||||
if(mf_classic_key_cache_get_next_key(
|
||||
instance->mfc_key_cache, §or_num, &key, &key_type)) {
|
||||
mfc_event->data->read_sector_request_data.sector_num = sector_num;
|
||||
mfc_event->data->read_sector_request_data.key = key;
|
||||
mfc_event->data->read_sector_request_data.key_type = key_type;
|
||||
mfc_event->data->read_sector_request_data.key_provided = true;
|
||||
} else {
|
||||
mfc_event->data->read_sector_request_data.key_provided = false;
|
||||
}
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
command = NfcCommandStop;
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeFail) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_classic(NfcGenericEvent event, void* context);
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#include "nfc_cli_dump_mf_desfire.h"
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire_poller.h>
|
||||
|
||||
#define TAG "MFDES"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfDesfire);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const MfDesfirePollerEvent* mf_desfire_event = event.event_data;
|
||||
|
||||
if(mf_desfire_event->type == MfDesfirePollerEventTypeReadSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandReset;
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_desfire(NfcGenericEvent event, void* context);
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "nfc_cli_dump_mf_plus.h"
|
||||
#include <nfc/protocols/mf_plus/mf_plus_poller.h>
|
||||
|
||||
#define TAG "MFPLUS"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_plus(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol == NfcProtocolMfPlus);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const MfPlusPollerEvent* mf_plus_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller));
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandReset;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_plus(NfcGenericEvent event, void* context);
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#include "nfc_cli_dump_mf_ultralight.h"
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
|
||||
|
||||
#define TAG "MFU"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfUltralight);
|
||||
NfcCliDumpContext* instance = context;
|
||||
const MfUltralightPollerEvent* mf_ultralight_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
|
||||
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
|
||||
|
||||
const NfcCliDumpAuthContext* auth_ctx = &instance->auth_ctx;
|
||||
mf_ultralight_event->data->auth_context.skip_auth = auth_ctx->skip_auth;
|
||||
mf_ultralight_event->data->auth_context.password = auth_ctx->key.password;
|
||||
mf_ultralight_event->data->auth_context.tdes_key = auth_ctx->key.tdes_key;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthFailed) {
|
||||
instance->result = NfcCliDumpErrorAuthFailed;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadFailed) {
|
||||
instance->result = NfcCliDumpErrorAuthFailed;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_mf_ultralight(NfcGenericEvent event, void* context);
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "../../../../helpers/mf_classic_key_cache.h"
|
||||
#include "../../helpers/nfc_cli_scanner.h"
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
#include <nfc/protocols/nfc_protocol.h>
|
||||
#include <nfc/nfc_device.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#include <nfc/protocols/felica/felica.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define NFC_CLI_DUMP_KEY_MAX_SIZE (16)
|
||||
|
||||
typedef union {
|
||||
MfUltralightAuthPassword password;
|
||||
FelicaCardKey felica_key;
|
||||
MfUltralightC3DesAuthKey tdes_key;
|
||||
uint8_t key[NFC_CLI_DUMP_KEY_MAX_SIZE];
|
||||
} NfcCliDumpKeyUnion;
|
||||
|
||||
typedef struct {
|
||||
NfcCliDumpKeyUnion key;
|
||||
uint8_t key_size;
|
||||
bool skip_auth;
|
||||
} NfcCliDumpAuthContext;
|
||||
|
||||
typedef enum {
|
||||
NfcCliDumpErrorNone,
|
||||
NfcCliDumpErrorTimeout,
|
||||
NfcCliDumpErrorNotPresent,
|
||||
NfcCliDumpErrorFailedToRead,
|
||||
NfcCliDumpErrorAuthFailed,
|
||||
|
||||
NfcCliDumpErrorNum,
|
||||
} NfcCliDumpError;
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
FuriString* file_path;
|
||||
Storage* storage;
|
||||
NfcCliScanner* scanner;
|
||||
NfcProtocol desired_protocol;
|
||||
uint32_t timeout;
|
||||
FuriSemaphore* sem_done;
|
||||
|
||||
NfcCliDumpError result;
|
||||
|
||||
NfcCliDumpAuthContext auth_ctx;
|
||||
MfClassicKeyCache* mfc_key_cache;
|
||||
|
||||
NfcPoller* poller;
|
||||
NfcDevice* nfc_device;
|
||||
} NfcCliDumpContext;
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "nfc_cli_dump_slix.h"
|
||||
#include <nfc/protocols/slix/slix_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolSlix);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const SlixPollerEvent* slix_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(slix_event->type == SlixPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller));
|
||||
command = NfcCommandStop;
|
||||
} else if(slix_event->type == SlixPollerEventTypeError) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context);
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "nfc_cli_dump_st25tb.h"
|
||||
#include <nfc/protocols/st25tb/st25tb_poller.h>
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolSt25tb);
|
||||
|
||||
NfcCliDumpContext* instance = context;
|
||||
const St25tbPollerEvent* st25tb_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {
|
||||
st25tb_event->data->mode_request.mode = St25tbPollerModeRead;
|
||||
} else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));
|
||||
instance->result = NfcCliDumpErrorNone;
|
||||
command = NfcCommandStop;
|
||||
} else if(st25tb_event->type == St25tbPollerEventTypeFailure) {
|
||||
instance->result = NfcCliDumpErrorFailedToRead;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context);
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "nfc_cli_format.h"
|
||||
|
||||
static const char* protocol_names[NfcProtocolNum] = {
|
||||
[NfcProtocolIso14443_3a] = "Iso14443-3a",
|
||||
[NfcProtocolIso14443_3b] = "Iso14443-3b",
|
||||
[NfcProtocolIso14443_4a] = "Iso14443-4a",
|
||||
[NfcProtocolIso14443_4b] = "Iso14443-4b",
|
||||
[NfcProtocolIso15693_3] = "Iso15693-3",
|
||||
[NfcProtocolFelica] = "FeliCa",
|
||||
[NfcProtocolMfUltralight] = "Mifare Ultralight",
|
||||
[NfcProtocolMfClassic] = "Mifare Classic",
|
||||
[NfcProtocolMfDesfire] = "Mifare DESFire",
|
||||
[NfcProtocolMfPlus] = "Mifare Plus",
|
||||
[NfcProtocolSlix] = "Slix",
|
||||
[NfcProtocolSt25tb] = "St25tb",
|
||||
};
|
||||
|
||||
const char* nfc_cli_get_protocol_name(NfcProtocol protocol) {
|
||||
furi_assert(protocol < NfcProtocolNum);
|
||||
return protocol_names[protocol];
|
||||
}
|
||||
|
||||
static const char* mf_ultralight_error_names[] = {
|
||||
[MfUltralightErrorNone] = "OK",
|
||||
[MfUltralightErrorNotPresent] = "Card not present",
|
||||
[MfUltralightErrorProtocol] = "Protocol failure",
|
||||
[MfUltralightErrorAuth] = "Auth failed",
|
||||
[MfUltralightErrorTimeout] = "Timeout",
|
||||
};
|
||||
|
||||
const char* nfc_cli_mf_ultralight_get_error(MfUltralightError error) {
|
||||
furi_assert(error < COUNT_OF(mf_ultralight_error_names));
|
||||
return mf_ultralight_error_names[error];
|
||||
}
|
||||
|
||||
void nfc_cli_format_array(
|
||||
const uint8_t* data,
|
||||
const size_t data_size,
|
||||
const char* header,
|
||||
FuriString* output) {
|
||||
furi_assert(data);
|
||||
furi_assert(data_size > 0);
|
||||
furi_assert(header);
|
||||
furi_assert(output);
|
||||
|
||||
furi_string_cat_printf(output, "%s", header);
|
||||
for(size_t i = 0; i < data_size; i++) {
|
||||
furi_string_cat_printf(output, "%02X ", data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_cli_printf_array(const uint8_t* data, const size_t data_size, const char* header) {
|
||||
furi_assert(data);
|
||||
furi_assert(data_size > 0);
|
||||
furi_assert(header);
|
||||
|
||||
printf("%s", header);
|
||||
for(size_t i = 0; i < data_size; i++) {
|
||||
printf("%02X ", data[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <nfc/protocols/nfc_protocol.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
|
||||
const char* nfc_cli_get_protocol_name(NfcProtocol protocol);
|
||||
const char* nfc_cli_mf_ultralight_get_error(MfUltralightError error);
|
||||
|
||||
void nfc_cli_format_array(
|
||||
const uint8_t* data,
|
||||
const size_t data_size,
|
||||
const char* header,
|
||||
FuriString* output);
|
||||
|
||||
void nfc_cli_printf_array(const uint8_t* data, const size_t data_size, const char* header);
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "nfc_cli_protocol_parser.h"
|
||||
|
||||
#include <m-bptree.h>
|
||||
|
||||
#define PROTOCOLS_TREE_RANK 4
|
||||
|
||||
BPTREE_DEF2(
|
||||
ProtocolTree,
|
||||
PROTOCOLS_TREE_RANK,
|
||||
FuriString*,
|
||||
FURI_STRING_OPLIST,
|
||||
NfcProtocol,
|
||||
M_POD_OPLIST);
|
||||
|
||||
#define M_OPL_ProtocolTree_t() BPTREE_OPLIST(ProtocolTree, M_POD_OPLIST)
|
||||
|
||||
struct NfcCliProtocolParser {
|
||||
ProtocolTree_t protocols;
|
||||
};
|
||||
|
||||
NfcCliProtocolParser* nfc_cli_protocol_parser_alloc(
|
||||
const NfcProtocolNameValuePair* valid_protocols,
|
||||
const size_t valid_count) {
|
||||
furi_assert(valid_protocols);
|
||||
furi_assert(valid_count > 0);
|
||||
|
||||
NfcCliProtocolParser* instance = malloc(sizeof(NfcCliProtocolParser));
|
||||
|
||||
FuriString* name = furi_string_alloc();
|
||||
ProtocolTree_init(instance->protocols);
|
||||
for(size_t i = 0; i < valid_count; i++) {
|
||||
const NfcProtocolNameValuePair* item = &valid_protocols[i];
|
||||
furi_string_set_str(name, item->name);
|
||||
ProtocolTree_set_at(instance->protocols, name, item->value);
|
||||
}
|
||||
|
||||
furi_string_free(name);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_protocol_parser_free(NfcCliProtocolParser* instance) {
|
||||
furi_assert(instance);
|
||||
ProtocolTree_clear(instance->protocols);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
bool nfc_cli_protocol_parser_get(
|
||||
NfcCliProtocolParser* instance,
|
||||
FuriString* key,
|
||||
NfcProtocol* result) {
|
||||
furi_assert(instance);
|
||||
furi_assert(key);
|
||||
|
||||
NfcProtocol* protocol = ProtocolTree_get(instance->protocols, key);
|
||||
if(protocol) *result = *protocol;
|
||||
|
||||
return protocol != NULL;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <nfc/protocols/nfc_protocol.h>
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
NfcProtocol value;
|
||||
} NfcProtocolNameValuePair;
|
||||
|
||||
typedef struct NfcCliProtocolParser NfcCliProtocolParser;
|
||||
|
||||
NfcCliProtocolParser* nfc_cli_protocol_parser_alloc(
|
||||
const NfcProtocolNameValuePair* valid_protocols,
|
||||
const size_t valid_count);
|
||||
|
||||
void nfc_cli_protocol_parser_free(NfcCliProtocolParser* instance);
|
||||
bool nfc_cli_protocol_parser_get(
|
||||
NfcCliProtocolParser* instance,
|
||||
FuriString* key,
|
||||
NfcProtocol* result);
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "nfc_cli_scanner.h"
|
||||
#include <nfc/nfc_scanner.h>
|
||||
#include "nfc_cli_format.h"
|
||||
|
||||
#define NFC_CLI_SCANNER_FLAG_DETECTED (1UL << 0)
|
||||
|
||||
struct NfcCliScanner {
|
||||
Nfc* nfc;
|
||||
size_t protocols_detected_num;
|
||||
NfcProtocol protocols_detected[NfcProtocolNum];
|
||||
FuriThreadId thread_id;
|
||||
NfcScanner* scanner;
|
||||
};
|
||||
|
||||
NfcCliScanner* nfc_cli_scanner_alloc(Nfc* nfc) {
|
||||
NfcCliScanner* instance = malloc(sizeof(NfcCliScanner));
|
||||
instance->nfc = nfc;
|
||||
instance->thread_id = furi_thread_get_current_id();
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_scanner_free(NfcCliScanner* instance) {
|
||||
furi_assert(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static void nfc_cli_scanner_detect_callback(NfcScannerEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
NfcCliScanner* instance = context;
|
||||
|
||||
if(event.type == NfcScannerEventTypeDetected) {
|
||||
instance->protocols_detected_num = event.data.protocol_num;
|
||||
memcpy(
|
||||
instance->protocols_detected,
|
||||
event.data.protocols,
|
||||
event.data.protocol_num * sizeof(NfcProtocol));
|
||||
furi_thread_flags_set(instance->thread_id, NFC_CLI_SCANNER_FLAG_DETECTED);
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_cli_scanner_detect_protocol(NfcCliScanner* instance, uint32_t timeout) {
|
||||
instance->scanner = nfc_scanner_alloc(instance->nfc);
|
||||
nfc_scanner_start(instance->scanner, nfc_cli_scanner_detect_callback, instance);
|
||||
uint32_t event =
|
||||
furi_thread_flags_wait(NFC_CLI_SCANNER_FLAG_DETECTED, FuriFlagWaitAny, timeout);
|
||||
nfc_scanner_stop(instance->scanner);
|
||||
nfc_scanner_free(instance->scanner);
|
||||
return (event == NFC_CLI_SCANNER_FLAG_DETECTED);
|
||||
}
|
||||
|
||||
void nfc_cli_scanner_begin_scan(NfcCliScanner* instance) {
|
||||
instance->scanner = nfc_scanner_alloc(instance->nfc);
|
||||
nfc_scanner_start(instance->scanner, nfc_cli_scanner_detect_callback, instance);
|
||||
}
|
||||
|
||||
bool nfc_cli_scanner_wait_scan(NfcCliScanner* instance, uint32_t timeout) {
|
||||
UNUSED(instance);
|
||||
uint32_t event =
|
||||
furi_thread_flags_wait(NFC_CLI_SCANNER_FLAG_DETECTED, FuriFlagWaitAny, timeout);
|
||||
return (event == NFC_CLI_SCANNER_FLAG_DETECTED);
|
||||
}
|
||||
|
||||
void nfc_cli_scanner_end_scan(NfcCliScanner* instance) {
|
||||
nfc_scanner_stop(instance->scanner);
|
||||
nfc_scanner_free(instance->scanner);
|
||||
}
|
||||
|
||||
void nfc_cli_scanner_list_detected_protocols(NfcCliScanner* instance) {
|
||||
printf("Protocols detected: ");
|
||||
size_t n = instance->protocols_detected_num;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
const char* name = nfc_cli_get_protocol_name(instance->protocols_detected[i]);
|
||||
printf((i == (n - 1)) ? "%s\r\n" : "%s, ", name);
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_cli_scanner_protocol_was_detected(NfcCliScanner* instance, NfcProtocol protocol) {
|
||||
furi_assert(instance);
|
||||
furi_assert(protocol < NfcProtocolNum);
|
||||
|
||||
for(size_t i = 0; i < instance->protocols_detected_num; i++) {
|
||||
if(instance->protocols_detected[i] == protocol) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NfcProtocol nfc_cli_scanner_get_protocol(NfcCliScanner* instance, size_t idx) {
|
||||
furi_assert(instance);
|
||||
furi_assert(idx < instance->protocols_detected_num);
|
||||
return instance->protocols_detected[idx];
|
||||
}
|
||||
|
||||
size_t nfc_cli_scanner_detected_protocol_num(NfcCliScanner* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->protocols_detected_num;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <nfc.h>
|
||||
#include <nfc/protocols/nfc_protocol.h>
|
||||
|
||||
typedef struct NfcCliScanner NfcCliScanner;
|
||||
|
||||
NfcCliScanner* nfc_cli_scanner_alloc(Nfc* nfc);
|
||||
void nfc_cli_scanner_free(NfcCliScanner* instance);
|
||||
|
||||
bool nfc_cli_scanner_detect_protocol(NfcCliScanner* instance, uint32_t timeout);
|
||||
|
||||
void nfc_cli_scanner_begin_scan(NfcCliScanner* instance);
|
||||
bool nfc_cli_scanner_wait_scan(NfcCliScanner* instance, uint32_t timeout);
|
||||
void nfc_cli_scanner_end_scan(NfcCliScanner* instance);
|
||||
|
||||
void nfc_cli_scanner_list_detected_protocols(NfcCliScanner* instance);
|
||||
size_t nfc_cli_scanner_detected_protocol_num(NfcCliScanner* instance);
|
||||
bool nfc_cli_scanner_protocol_was_detected(NfcCliScanner* instance, NfcProtocol protocol);
|
||||
NfcProtocol nfc_cli_scanner_get_protocol(NfcCliScanner* instance, size_t idx);
|
||||
@@ -0,0 +1,227 @@
|
||||
|
||||
#include "nfc_cli_action_info.h"
|
||||
|
||||
#include "../../../helpers/protocol_support/mf_ultralight/mf_ultralight_render.h"
|
||||
#include "../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <storage/storage.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
||||
|
||||
#define TAG "INFO"
|
||||
|
||||
typedef struct {
|
||||
uint8_t magic;
|
||||
union {
|
||||
uint8_t value;
|
||||
struct {
|
||||
uint8_t minor : 4;
|
||||
uint8_t major : 4;
|
||||
};
|
||||
} version;
|
||||
|
||||
uint8_t size;
|
||||
|
||||
union {
|
||||
uint8_t value;
|
||||
struct {
|
||||
uint8_t write : 4;
|
||||
uint8_t read : 4;
|
||||
};
|
||||
} access;
|
||||
} FURI_PACKED MfUltralightCapabilityContainer;
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
MfUltralightData* data;
|
||||
} NfcCliMfuContext;
|
||||
|
||||
static void nfc_cli_mfu_info_get_vendor(const uint8_t vendor_key, FuriString* output) {
|
||||
furi_assert(output);
|
||||
|
||||
FuriString* buf = furi_string_alloc();
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(ff, EXT_PATH("nfc/assets/vendors.nfc"))) {
|
||||
FURI_LOG_W(TAG, "NFC Vendors dict not found");
|
||||
break;
|
||||
}
|
||||
|
||||
char uid_str[5];
|
||||
snprintf(uid_str, sizeof(uid_str), "%d", vendor_key);
|
||||
|
||||
if(flipper_format_read_string(ff, uid_str, buf))
|
||||
furi_string_printf(output, "%s, %s", uid_str, furi_string_get_cstr(buf));
|
||||
else
|
||||
furi_string_printf(output, "unknown");
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_string_free(buf);
|
||||
}
|
||||
|
||||
const char*
|
||||
nfc_cli_mfu_capability_container_get_access_description(const uint8_t value, bool read) {
|
||||
const char* description = "RFU"; //value 0x01 - 0x07, and 0xF when read
|
||||
if(value == 0x00)
|
||||
description = "access fully granted";
|
||||
else if(value >= 0x08 && value <= 0x0E)
|
||||
description = "proprietary";
|
||||
else if(value == 0x0F && !read)
|
||||
description = "no access granted at all";
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
static void nfc_cli_mfu_info_print_common(const MfUltralightData* data) {
|
||||
FuriString* str = furi_string_alloc();
|
||||
|
||||
printf(ANSI_FG_GREEN "\r\n\tTag information\r\n" ANSI_RESET);
|
||||
printf(
|
||||
"Type: " ANSI_FG_YELLOW "%s\r\n" ANSI_RESET,
|
||||
mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull));
|
||||
|
||||
nfc_cli_mfu_info_get_vendor(data->iso14443_3a_data->uid[0], str);
|
||||
printf("Vendor ID: %s\r\n", furi_string_get_cstr(str));
|
||||
|
||||
furi_string_reset(str);
|
||||
nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, str);
|
||||
printf("%s\r\n", furi_string_get_cstr(str));
|
||||
printf("BCC0: %02X\r\nBCC1: %02X\r\n", data->page[0].data[3], data->page[2].data[0]);
|
||||
|
||||
furi_string_free(str);
|
||||
}
|
||||
|
||||
static void nfc_cli_mfu_info_print_ndef(const MfUltralightData* data) {
|
||||
const MfUltralightCapabilityContainer* cc =
|
||||
(const MfUltralightCapabilityContainer*)data->page[3].data;
|
||||
if(cc->magic == 0xE1) {
|
||||
printf(ANSI_FG_GREEN "\r\n\tNDEF Message\r\n" ANSI_RESET);
|
||||
nfc_cli_printf_array(data->page[3].data, 4, "Capability container: ");
|
||||
printf(
|
||||
"\r\nMagic number: %02X\r\nVersion %d.%d\r\nSize: [%02X] - %d bytes\r\n",
|
||||
cc->magic,
|
||||
cc->version.major,
|
||||
cc->version.minor,
|
||||
cc->size,
|
||||
cc->size * 8);
|
||||
printf(
|
||||
"Access read: [%02X] - %s",
|
||||
cc->access.read,
|
||||
nfc_cli_mfu_capability_container_get_access_description(cc->access.read, true));
|
||||
printf(
|
||||
"Access write: [%02X] - %s",
|
||||
cc->access.write,
|
||||
nfc_cli_mfu_capability_container_get_access_description(cc->access.write, false));
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_cli_mfu_info_print_counter(const MfUltralightData* data) {
|
||||
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
|
||||
if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadCounter)) return;
|
||||
|
||||
printf(ANSI_FG_GREEN "\r\n\n\tTag counters\r\n" ANSI_RESET);
|
||||
uint8_t i =
|
||||
mf_ultralight_support_feature(features, MfUltralightFeatureSupportSingleCounter) ? 2 : 0;
|
||||
|
||||
for(; i < MF_ULTRALIGHT_COUNTER_NUM; i++) {
|
||||
printf("Counter [%d]: ", i);
|
||||
nfc_cli_printf_array(data->counter[i].data, MF_ULTRALIGHT_COUNTER_SIZE, "");
|
||||
printf(" Value: %lu\r\n", data->counter[i].counter);
|
||||
|
||||
const uint8_t tf = data->tearing_flag[i].data;
|
||||
printf(
|
||||
"Tearing [%d]: [%02X] %s",
|
||||
i,
|
||||
tf,
|
||||
tf == MF_ULTRALIGHT_TEARING_FLAG_DEFAULT ? "(ok)" : "");
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_cli_mfu_info_print_signature(const MfUltralightData* data) {
|
||||
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
|
||||
if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadSignature)) return;
|
||||
|
||||
const MfUltralightSignature* signature = &data->signature;
|
||||
printf(ANSI_FG_GREEN "\r\n\n\tTag signature\r\n" ANSI_RESET);
|
||||
nfc_cli_printf_array(signature->data, sizeof(signature->data), "ECC signature: ");
|
||||
}
|
||||
|
||||
static void nfc_cli_mfu_info_print_version_storage_size(uint8_t storage_size) {
|
||||
uint16_t max_size = 1 << ((storage_size >> 1) + 1);
|
||||
uint16_t min_exact_size = 1 << (storage_size >> 1);
|
||||
|
||||
bool exact_size = !(storage_size & 0x01);
|
||||
if(exact_size)
|
||||
printf("[%02X], (%u bytes)", storage_size, min_exact_size);
|
||||
else
|
||||
printf("[%02X], (%u <-> %u bytes)", storage_size, min_exact_size, max_size);
|
||||
}
|
||||
|
||||
static void nfc_cli_mfu_info_print_version(const MfUltralightData* data) {
|
||||
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
|
||||
if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadVersion)) return;
|
||||
|
||||
const MfUltralightVersion* version = &data->version;
|
||||
printf(ANSI_FG_GREEN "\r\n\n\tTag Version\r\n" ANSI_RESET);
|
||||
nfc_cli_printf_array((uint8_t*)version, sizeof(MfUltralightVersion), "Raw bytes: ");
|
||||
|
||||
FuriString* str = furi_string_alloc();
|
||||
nfc_cli_mfu_info_get_vendor(version->vendor_id, str);
|
||||
printf("\r\nVendor ID: %s\r\n", furi_string_get_cstr(str));
|
||||
furi_string_free(str);
|
||||
|
||||
printf("Product type: %02X\r\n", version->prod_type);
|
||||
|
||||
printf(
|
||||
"Protocol type: %02X%s\r\n",
|
||||
version->protocol_type,
|
||||
(version->protocol_type == 0x3) ? ", ISO14443-3 Compliant" : "");
|
||||
|
||||
printf(
|
||||
"Product subtype: [%02X], %s\r\n",
|
||||
version->prod_subtype,
|
||||
(version->prod_subtype == 1) ? "17 pF" : "50pF");
|
||||
printf(
|
||||
"Major version: %02X\r\nMinor version: %02X\r\nSize: ",
|
||||
version->prod_ver_major,
|
||||
version->prod_ver_minor);
|
||||
nfc_cli_mfu_info_print_version_storage_size(version->storage_size);
|
||||
}
|
||||
|
||||
NfcCliActionContext* nfc_cli_mfu_info_alloc_ctx(Nfc* nfc) {
|
||||
NfcCliMfuContext* instance = malloc(sizeof(NfcCliMfuContext));
|
||||
instance->nfc = nfc;
|
||||
instance->data = mf_ultralight_alloc();
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_mfu_info_free_ctx(NfcCliActionContext* ctx) {
|
||||
NfcCliMfuContext* instance = ctx;
|
||||
mf_ultralight_free(instance->data);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_cli_mfu_info_execute(PipeSide* pipe, NfcCliActionContext* ctx) {
|
||||
furi_assert(pipe);
|
||||
furi_assert(ctx);
|
||||
|
||||
NfcCliMfuContext* instance = ctx;
|
||||
|
||||
MfUltralightError error =
|
||||
mf_ultralight_poller_sync_read_card(instance->nfc, instance->data, NULL);
|
||||
if(error == MfUltralightErrorNone) {
|
||||
const MfUltralightData* data = instance->data;
|
||||
nfc_cli_mfu_info_print_common(data);
|
||||
nfc_cli_mfu_info_print_ndef(data);
|
||||
nfc_cli_mfu_info_print_counter(data);
|
||||
nfc_cli_mfu_info_print_signature(data);
|
||||
nfc_cli_mfu_info_print_version(data);
|
||||
} else {
|
||||
printf(ANSI_FG_RED "Error: %s" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
NfcCliActionContext* nfc_cli_mfu_info_alloc_ctx(Nfc* nfc);
|
||||
void nfc_cli_mfu_info_free_ctx(NfcCliActionContext* ctx);
|
||||
void nfc_cli_mfu_info_execute(PipeSide* pipe, NfcCliActionContext* ctx);
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "nfc_cli_action_rdbl.h"
|
||||
|
||||
#include "../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <toolbox/strint.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
||||
|
||||
#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
uint16_t block;
|
||||
} NfcCliMfuRdblContext;
|
||||
|
||||
NfcCliActionContext* nfc_cli_mfu_rdbl_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliMfuRdblContext* instance = malloc(sizeof(NfcCliMfuRdblContext));
|
||||
instance->nfc = nfc;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_mfu_rdbl_free_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliMfuRdblContext* instance = ctx;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_cli_mfu_rdbl_execute(PipeSide* pipe, NfcCliActionContext* ctx) {
|
||||
furi_assert(pipe);
|
||||
|
||||
NfcCliMfuRdblContext* instance = ctx;
|
||||
MfUltralightPage page = {0};
|
||||
|
||||
MfUltralightError error =
|
||||
mf_ultralight_poller_sync_read_page(instance->nfc, instance->block, &page);
|
||||
|
||||
if(error == MfUltralightErrorNone) {
|
||||
printf("\r\nBlock: %d ", instance->block);
|
||||
nfc_cli_printf_array(page.data, sizeof(MfUltralightPage), "Data: ");
|
||||
printf("\r\n");
|
||||
} else {
|
||||
printf(ANSI_FG_RED "Error: %s" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_cli_mfu_rdbl_parse_block(FuriString* value, NfcCliActionContext* output) {
|
||||
NfcCliMfuRdblContext* ctx = output;
|
||||
|
||||
StrintParseError err = strint_to_uint16(furi_string_get_cstr(value), NULL, &ctx->block, 10);
|
||||
return err == StrintParseNoError;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
NfcCliActionContext* nfc_cli_mfu_rdbl_alloc_ctx(Nfc* nfc);
|
||||
void nfc_cli_mfu_rdbl_free_ctx(NfcCliActionContext* ctx);
|
||||
void nfc_cli_mfu_rdbl_execute(PipeSide* pipe, NfcCliActionContext* ctx);
|
||||
bool nfc_cli_mfu_rdbl_parse_block(FuriString* value, NfcCliActionContext* ctx);
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "nfc_cli_action_rdbl.h"
|
||||
|
||||
#include "../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <toolbox/args.h>
|
||||
#include <toolbox/strint.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
||||
|
||||
#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
uint16_t block;
|
||||
MfUltralightPage page;
|
||||
} NfcCliMfuWrblContext;
|
||||
|
||||
NfcCliActionContext* nfc_cli_mfu_wrbl_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliMfuWrblContext* instance = malloc(sizeof(NfcCliMfuWrblContext));
|
||||
instance->nfc = nfc;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_mfu_wrbl_free_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliMfuWrblContext* instance = ctx;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_cli_mfu_wrbl_execute(PipeSide* pipe, NfcCliActionContext* ctx) {
|
||||
furi_assert(pipe);
|
||||
|
||||
NfcCliMfuWrblContext* instance = ctx;
|
||||
|
||||
MfUltralightError error =
|
||||
mf_ultralight_poller_sync_write_page(instance->nfc, instance->block, &instance->page);
|
||||
|
||||
if(error == MfUltralightErrorNone) {
|
||||
printf(ANSI_FG_BR_GREEN "\r\nSuccess\r\n" ANSI_RESET);
|
||||
printf("Block: %d ", instance->block);
|
||||
nfc_cli_printf_array(instance->page.data, sizeof(MfUltralightPage), "Data: ");
|
||||
printf("\r\n");
|
||||
} else {
|
||||
printf(ANSI_FG_RED "Error: %s" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_cli_mfu_wrbl_parse_block(FuriString* value, NfcCliActionContext* output) {
|
||||
NfcCliMfuWrblContext* ctx = output;
|
||||
|
||||
StrintParseError err = strint_to_uint16(furi_string_get_cstr(value), NULL, &ctx->block, 10);
|
||||
return err == StrintParseNoError;
|
||||
}
|
||||
|
||||
bool nfc_cli_mfu_wrbl_parse_data(FuriString* value, void* output) {
|
||||
NfcCliMfuWrblContext* ctx = output;
|
||||
|
||||
bool result = false;
|
||||
do {
|
||||
size_t len = furi_string_size(value);
|
||||
if(len % 2 != 0) break;
|
||||
|
||||
size_t data_length = len / 2;
|
||||
if(data_length != MF_ULTRALIGHT_PAGE_SIZE) break;
|
||||
|
||||
result = args_read_hex_bytes(value, ctx->page.data, data_length);
|
||||
} while(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
NfcCliActionContext* nfc_cli_mfu_wrbl_alloc_ctx(Nfc* nfc);
|
||||
void nfc_cli_mfu_wrbl_free_ctx(NfcCliActionContext* ctx);
|
||||
void nfc_cli_mfu_wrbl_execute(PipeSide* pipe, NfcCliActionContext* ctx);
|
||||
bool nfc_cli_mfu_wrbl_parse_block(FuriString* value, NfcCliActionContext* ctx);
|
||||
bool nfc_cli_mfu_wrbl_parse_data(FuriString* value, void* output);
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "nfc_cli_command_mfu.h"
|
||||
#include "nfc_cli_action_info.h"
|
||||
#include "nfc_cli_action_rdbl.h"
|
||||
#include "nfc_cli_action_wrbl.h"
|
||||
|
||||
#define TAG "MFU"
|
||||
|
||||
//mfu info
|
||||
const NfcCliActionDescriptor info_action = {
|
||||
.name = "info",
|
||||
.description = "Get basic information about the card",
|
||||
.alloc = nfc_cli_mfu_info_alloc_ctx,
|
||||
.free = nfc_cli_mfu_info_free_ctx,
|
||||
.execute = nfc_cli_mfu_info_execute,
|
||||
.key_count = 0,
|
||||
.keys = NULL,
|
||||
};
|
||||
|
||||
const NfcCliKeyDescriptor rdbl_action_keys[] = {
|
||||
{
|
||||
.short_name = "b",
|
||||
.long_name = "block",
|
||||
.features = {.required = true, .parameter = true},
|
||||
.description = "desired block number",
|
||||
.parse = nfc_cli_mfu_rdbl_parse_block,
|
||||
},
|
||||
};
|
||||
|
||||
//mfu rdbl -b 0
|
||||
//mfu rdbl --block 0
|
||||
const NfcCliActionDescriptor rdbl_action = {
|
||||
.name = "rdbl",
|
||||
.description = "Read block from ultralight card",
|
||||
.alloc = nfc_cli_mfu_rdbl_alloc_ctx,
|
||||
.free = nfc_cli_mfu_rdbl_free_ctx,
|
||||
.execute = nfc_cli_mfu_rdbl_execute,
|
||||
.key_count = COUNT_OF(rdbl_action_keys),
|
||||
.keys = rdbl_action_keys,
|
||||
};
|
||||
|
||||
const NfcCliKeyDescriptor wrbl_action_keys[] = {
|
||||
{
|
||||
.short_name = "b",
|
||||
.long_name = "block",
|
||||
.features = {.required = true, .parameter = true},
|
||||
.description = "desired block number",
|
||||
.parse = nfc_cli_mfu_wrbl_parse_block,
|
||||
},
|
||||
{
|
||||
.short_name = "d",
|
||||
.long_name = "data",
|
||||
.features = {.required = true, .parameter = true},
|
||||
.description = "new data for block",
|
||||
.parse = nfc_cli_mfu_wrbl_parse_data,
|
||||
},
|
||||
};
|
||||
|
||||
//mfu wrbl -b 0 -d DEADBEAF
|
||||
//mfu rdbl --block 0 -- data DEADBEEF
|
||||
const NfcCliActionDescriptor wrbl_action = {
|
||||
.name = "wrbl",
|
||||
.description = "Read block from ultralight card",
|
||||
.alloc = nfc_cli_mfu_wrbl_alloc_ctx,
|
||||
.free = nfc_cli_mfu_wrbl_free_ctx,
|
||||
.execute = nfc_cli_mfu_wrbl_execute,
|
||||
.key_count = COUNT_OF(wrbl_action_keys),
|
||||
.keys = wrbl_action_keys,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor* mfu_actions[] = {
|
||||
&rdbl_action,
|
||||
&info_action,
|
||||
&wrbl_action,
|
||||
};
|
||||
|
||||
//Command descriptor
|
||||
ADD_NFC_CLI_COMMAND(mfu, "Mifare Ultralight specific commands", mfu_actions);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor mfu_cmd;
|
||||
@@ -0,0 +1,126 @@
|
||||
#include "nfc_cli_command_emulate.h"
|
||||
#include "helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc.h>
|
||||
#include <nfc_listener.h>
|
||||
#include <nfc_device.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
NfcDevice* nfc_device;
|
||||
FuriString* file_path;
|
||||
Storage* storage;
|
||||
} NfcCliEmulateContext;
|
||||
|
||||
static NfcCliActionContext* nfc_cli_emulate_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliEmulateContext* instance = malloc(sizeof(NfcCliEmulateContext));
|
||||
instance->nfc = nfc;
|
||||
instance->file_path = furi_string_alloc();
|
||||
instance->nfc_device = nfc_device_alloc();
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void nfc_cli_emulate_free_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliEmulateContext* instance = ctx;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_string_free(instance->file_path);
|
||||
nfc_device_free(instance->nfc_device);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static const NfcProtocol supported_protocols[] = {
|
||||
NfcProtocolIso14443_3a,
|
||||
NfcProtocolIso14443_4a,
|
||||
NfcProtocolIso15693_3,
|
||||
NfcProtocolMfUltralight,
|
||||
NfcProtocolMfClassic,
|
||||
NfcProtocolSlix,
|
||||
NfcProtocolFelica,
|
||||
};
|
||||
|
||||
static bool nfc_cli_emulate_protocol_supports_emulation(NfcProtocol protocol) {
|
||||
for(size_t i = 0; i < COUNT_OF(supported_protocols); i++) {
|
||||
if(supported_protocols[i] == protocol) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void nfc_cli_emulate_execute(PipeSide* pipe, NfcCliActionContext* context) {
|
||||
UNUSED(pipe);
|
||||
furi_assert(context);
|
||||
NfcCliEmulateContext* instance = context;
|
||||
do {
|
||||
const char* path = furi_string_get_cstr(instance->file_path);
|
||||
if(!storage_common_exists(instance->storage, path)) {
|
||||
printf(ANSI_FG_RED "Wrong path \'%s\'.\r\n" ANSI_RESET, path);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!nfc_device_load(instance->nfc_device, path)) {
|
||||
printf(ANSI_FG_RED "Failed to load \'%s\'.\r\n" ANSI_RESET, path);
|
||||
break;
|
||||
}
|
||||
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
|
||||
if(!nfc_cli_emulate_protocol_supports_emulation(protocol)) {
|
||||
printf(
|
||||
ANSI_FG_RED "Error. Emulation for %s is not supported\r\n" ANSI_RESET,
|
||||
nfc_cli_get_protocol_name(protocol));
|
||||
break;
|
||||
}
|
||||
|
||||
const NfcDeviceData* data = nfc_device_get_data(instance->nfc_device, protocol);
|
||||
NfcListener* listener = nfc_listener_alloc(instance->nfc, protocol, data);
|
||||
|
||||
nfc_listener_start(listener, NULL, NULL);
|
||||
printf("\r\nEmulating. Press Ctrl+C to abort\r\n");
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
nfc_listener_stop(listener);
|
||||
nfc_listener_free(listener);
|
||||
} while(false);
|
||||
}
|
||||
|
||||
static bool nfc_cli_emulate_parse_filename_key(FuriString* value, void* output) {
|
||||
furi_assert(value);
|
||||
furi_assert(output);
|
||||
NfcCliEmulateContext* ctx = output;
|
||||
furi_string_set(ctx->file_path, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
const NfcCliKeyDescriptor emulate_keys[] = {
|
||||
{
|
||||
.features = {.required = true, .parameter = true},
|
||||
.long_name = "file",
|
||||
.short_name = "f",
|
||||
.description = "path to new file",
|
||||
.parse = nfc_cli_emulate_parse_filename_key,
|
||||
},
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor emulate_action = {
|
||||
.name = "emulate",
|
||||
.description = "Emulate .nfc file content",
|
||||
.alloc = nfc_cli_emulate_alloc_ctx,
|
||||
.free = nfc_cli_emulate_free_ctx,
|
||||
.execute = nfc_cli_emulate_execute,
|
||||
.key_count = COUNT_OF(emulate_keys),
|
||||
.keys = emulate_keys,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor* emulate_actions_collection[] = {&emulate_action};
|
||||
|
||||
//Command descriptor
|
||||
ADD_NFC_CLI_COMMAND(emulate, "", emulate_actions_collection);
|
||||
|
||||
//Command usage: emulate [-f <file>]
|
||||
//Command examples:
|
||||
//emulate -f ext/nfc/test.nfc
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor emulate_cmd;
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "nfc_cli_command_field.h"
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
static void nfc_cli_field(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
furi_hal_nfc_low_power_mode_stop();
|
||||
furi_hal_nfc_poller_field_on();
|
||||
|
||||
printf("Field is on. Don't leave device in this mode for too long.\r\n");
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
furi_hal_nfc_low_power_mode_start();
|
||||
}
|
||||
|
||||
const NfcCliCommandDescriptor field_cmd = {
|
||||
.name = "field",
|
||||
.description = "Turns NFC field on",
|
||||
.callback = nfc_cli_field,
|
||||
.action_count = 0,
|
||||
.actions = NULL,
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor field_cmd;
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
#include "nfc_cli_command_scanner.h"
|
||||
#include "helpers/nfc_cli_scanner.h"
|
||||
#include "helpers/nfc_cli_format.h"
|
||||
|
||||
typedef struct {
|
||||
NfcCliScanner* scanner;
|
||||
bool display_tree;
|
||||
} NfcCliCmdScannerContext;
|
||||
|
||||
static NfcCliActionContext* nfc_cli_command_scanner_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliCmdScannerContext* instance = malloc(sizeof(NfcCliCmdScannerContext));
|
||||
instance->scanner = nfc_cli_scanner_alloc(nfc);
|
||||
instance->display_tree = false;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void nfc_cli_command_scanner_free_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliCmdScannerContext* instance = ctx;
|
||||
nfc_cli_scanner_free(instance->scanner);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_cli_command_scanner_format_protocol_tree(NfcProtocol protocol, FuriString* output) {
|
||||
const char* names[10] = {0};
|
||||
uint8_t cnt = 0;
|
||||
while(protocol != NfcProtocolInvalid) {
|
||||
names[cnt++] = nfc_cli_get_protocol_name(protocol);
|
||||
protocol = nfc_protocol_get_parent(protocol);
|
||||
}
|
||||
|
||||
for(int8_t i = cnt - 1; i >= 0; i--) {
|
||||
furi_string_cat_printf(output, (i == 0) ? "%s" : "%s -> ", names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_cli_command_scanner_format_detected_protocols(NfcCliScanner* instance) {
|
||||
FuriString* str = furi_string_alloc();
|
||||
printf("Protocols detected: \r\n");
|
||||
for(size_t i = 0; i < nfc_cli_scanner_detected_protocol_num(instance); i++) {
|
||||
furi_string_reset(str);
|
||||
NfcProtocol protocol = nfc_cli_scanner_get_protocol(instance, i);
|
||||
nfc_cli_command_scanner_format_protocol_tree(protocol, str);
|
||||
printf("Protocol [%zu]: %s\r\n", i + 1, furi_string_get_cstr(str));
|
||||
}
|
||||
furi_string_free(str);
|
||||
}
|
||||
|
||||
static void nfc_cli_command_scanner_execute(PipeSide* pipe, void* context) {
|
||||
NfcCliCmdScannerContext* instance = context;
|
||||
|
||||
printf("Press Ctrl+C to abort\r\n\n");
|
||||
nfc_cli_scanner_begin_scan(instance->scanner);
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe) &&
|
||||
!nfc_cli_scanner_wait_scan(instance->scanner, 50))
|
||||
;
|
||||
nfc_cli_scanner_end_scan(instance->scanner);
|
||||
|
||||
if(!instance->display_tree)
|
||||
nfc_cli_scanner_list_detected_protocols(instance->scanner);
|
||||
else
|
||||
nfc_cli_command_scanner_format_detected_protocols(instance->scanner);
|
||||
}
|
||||
|
||||
static bool nfc_cli_command_scanner_parse_tree(FuriString* value, void* output) {
|
||||
UNUSED(value);
|
||||
NfcCliCmdScannerContext* ctx = output;
|
||||
ctx->display_tree = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const NfcCliKeyDescriptor tree_key = {
|
||||
.short_name = "t",
|
||||
.long_name = "tree",
|
||||
.features = {.parameter = false, .required = false, .multivalue = false},
|
||||
.description = "displays protocol hierarchy for each detected protocol",
|
||||
.parse = nfc_cli_command_scanner_parse_tree,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor scanner_action = {
|
||||
.name = "scanner",
|
||||
.description = "Detect tag type",
|
||||
.key_count = 1,
|
||||
.keys = &tree_key,
|
||||
.execute = nfc_cli_command_scanner_execute,
|
||||
.alloc = nfc_cli_command_scanner_alloc_ctx,
|
||||
.free = nfc_cli_command_scanner_free_ctx,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor* scanner_actions_collection[] = {&scanner_action};
|
||||
|
||||
ADD_NFC_CLI_COMMAND(scanner, "", scanner_actions_collection);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor scanner_cmd;
|
||||
@@ -0,0 +1,352 @@
|
||||
#include "nfc_cli_command_raw.h"
|
||||
#include "../helpers/nfc_cli_format.h"
|
||||
#include "../helpers/nfc_cli_protocol_parser.h"
|
||||
|
||||
#include "protocol_handlers/nfc_cli_raw_common_types.h"
|
||||
#include "protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.h"
|
||||
#include "protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.h"
|
||||
#include "protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.h"
|
||||
#include "protocol_handlers/felica/nfc_cli_raw_felica.h"
|
||||
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)
|
||||
|
||||
#define TAG "RAW"
|
||||
|
||||
typedef enum {
|
||||
NfcCliProtocolRequestTypeNormalExecute,
|
||||
NfcCliProtocolRequestTypeAbort,
|
||||
} NfcCliProtocolRequestType;
|
||||
|
||||
typedef enum {
|
||||
NfcPollerStateStopped,
|
||||
NfcPollerStateStarted,
|
||||
} NfcPollerState;
|
||||
|
||||
typedef NfcCommand (*NfcCliRawProtocolSpecificHandler)(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response);
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
NfcCliRawRequest request;
|
||||
NfcCliRawResponse response;
|
||||
|
||||
NfcPoller* poller;
|
||||
NfcPollerState poller_state;
|
||||
|
||||
NfcCliProtocolRequestType request_type;
|
||||
FuriMessageQueue* input_queue;
|
||||
FuriSemaphore* sem_done;
|
||||
|
||||
} NfcCliRawCmdContext;
|
||||
|
||||
static const char* raw_error_names[] = {
|
||||
[NfcCliRawErrorNone] = "None",
|
||||
[NfcCliRawErrorTimeout] = "Timeout",
|
||||
[NfcCliRawErrorProtocol] = "Internal protocol",
|
||||
[NfcCliRawErrorWrongCrc] = "Wrong CRC",
|
||||
[NfcCliRawErrorNotPresent] = "No card",
|
||||
};
|
||||
|
||||
static NfcCliActionContext* nfc_cli_raw_alloc_ctx(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliRawCmdContext* instance = malloc(sizeof(NfcCliRawCmdContext));
|
||||
instance->nfc = nfc;
|
||||
|
||||
instance->request.protocol = NfcProtocolInvalid;
|
||||
|
||||
instance->request.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
|
||||
instance->response.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
|
||||
|
||||
instance->input_queue = furi_message_queue_alloc(5, sizeof(NfcCliProtocolRequestType));
|
||||
instance->sem_done = furi_semaphore_alloc(1, 0);
|
||||
instance->response.activation_string = furi_string_alloc();
|
||||
instance->request.timeout = 0;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void nfc_cli_raw_abort_nfc_thread(NfcCliRawCmdContext* instance) {
|
||||
if(instance->poller_state == NfcPollerStateStarted) {
|
||||
instance->request_type = NfcCliProtocolRequestTypeAbort;
|
||||
furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);
|
||||
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
|
||||
instance->poller_state = NfcPollerStateStopped;
|
||||
}
|
||||
if(instance->poller) nfc_poller_stop(instance->poller);
|
||||
}
|
||||
|
||||
static void nfc_cli_raw_free_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliRawCmdContext* instance = ctx;
|
||||
|
||||
nfc_cli_raw_abort_nfc_thread(instance);
|
||||
if(instance->poller) nfc_poller_free(instance->poller);
|
||||
|
||||
furi_message_queue_free(instance->input_queue);
|
||||
furi_semaphore_free(instance->sem_done);
|
||||
|
||||
furi_string_free(instance->response.activation_string);
|
||||
bit_buffer_free(instance->response.rx_buffer);
|
||||
bit_buffer_free(instance->request.tx_buffer);
|
||||
instance->nfc = NULL;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool nfc_cli_raw_can_reuse_ctx(NfcCliActionContext* ctx) {
|
||||
furi_assert(ctx);
|
||||
NfcCliRawCmdContext* instance = ctx;
|
||||
NfcCliRawRequest* request = &instance->request;
|
||||
|
||||
bool result = request->keep_field;
|
||||
request->keep_field = false;
|
||||
request->append_crc = false;
|
||||
request->select = false;
|
||||
instance->request.timeout = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
const NfcCliRawProtocolSpecificHandler nfc_cli_raw_protocol_handlers[] = {
|
||||
[NfcProtocolIso14443_3a] = nfc_cli_raw_iso14443_3a_handler,
|
||||
[NfcProtocolIso14443_3b] = nfc_cli_raw_iso14443_3b_handler,
|
||||
[NfcProtocolIso14443_4a] = NULL,
|
||||
[NfcProtocolIso14443_4b] = NULL,
|
||||
[NfcProtocolIso15693_3] = nfc_cli_raw_iso15693_3_handler,
|
||||
[NfcProtocolFelica] = nfc_cli_raw_felica_handler,
|
||||
[NfcProtocolMfUltralight] = NULL,
|
||||
[NfcProtocolMfClassic] = NULL,
|
||||
[NfcProtocolMfDesfire] = NULL,
|
||||
[NfcProtocolSlix] = NULL,
|
||||
[NfcProtocolSt25tb] = NULL,
|
||||
};
|
||||
|
||||
static NfcCommand nfc_cli_raw_poller_callback(NfcGenericEventEx event, void* context) {
|
||||
NfcEvent* nfc_event = event.parent_event_data;
|
||||
NfcCliRawCmdContext* instance = context;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||
FURI_LOG_D(TAG, "Poller callback");
|
||||
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;
|
||||
furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);
|
||||
|
||||
if(request_type == NfcCliProtocolRequestTypeAbort) {
|
||||
command = NfcCommandStop;
|
||||
} else {
|
||||
const NfcCliRawProtocolSpecificHandler handler =
|
||||
nfc_cli_raw_protocol_handlers[instance->request.protocol];
|
||||
if(handler) handler(event.poller, &instance->request, &instance->response);
|
||||
}
|
||||
}
|
||||
furi_semaphore_release(instance->sem_done);
|
||||
if(command == NfcCommandStop) {
|
||||
FURI_LOG_D(TAG, "Aborting poller callback");
|
||||
instance->poller_state = NfcPollerStateStopped;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
static inline void nfc_cli_raw_print_result(const NfcCliRawCmdContext* instance) {
|
||||
if(!furi_string_empty(instance->response.activation_string))
|
||||
printf("%s\r\n", furi_string_get_cstr(instance->response.activation_string));
|
||||
|
||||
nfc_cli_printf_array(
|
||||
bit_buffer_get_data(instance->request.tx_buffer),
|
||||
bit_buffer_get_size_bytes(instance->request.tx_buffer),
|
||||
"Tx: ");
|
||||
|
||||
if(instance->response.result != NfcCliRawErrorNone)
|
||||
printf("\r\nError: \"%s\"\r\n", raw_error_names[instance->response.result]);
|
||||
|
||||
size_t rx_size = bit_buffer_get_size_bytes(instance->response.rx_buffer);
|
||||
if(rx_size > 0) {
|
||||
nfc_cli_printf_array(
|
||||
bit_buffer_get_data(instance->response.rx_buffer),
|
||||
bit_buffer_get_size_bytes(instance->response.rx_buffer),
|
||||
"\r\nRx: ");
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_cli_raw_execute(PipeSide* pipe, void* context) {
|
||||
UNUSED(pipe);
|
||||
furi_assert(context);
|
||||
NfcCliRawCmdContext* instance = context;
|
||||
|
||||
furi_string_reset(instance->response.activation_string);
|
||||
|
||||
if(instance->poller_state == NfcPollerStateStopped) {
|
||||
if(instance->poller == NULL)
|
||||
instance->poller = nfc_poller_alloc(instance->nfc, instance->request.protocol);
|
||||
|
||||
nfc_poller_start_ex(instance->poller, nfc_cli_raw_poller_callback, instance);
|
||||
instance->poller_state = NfcPollerStateStarted;
|
||||
}
|
||||
|
||||
instance->request_type = NfcCliProtocolRequestTypeNormalExecute;
|
||||
furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);
|
||||
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
|
||||
|
||||
nfc_cli_raw_print_result(instance);
|
||||
}
|
||||
|
||||
static const NfcProtocolNameValuePair supported_protocols[] = {
|
||||
{.name = "14a", .value = NfcProtocolIso14443_3a},
|
||||
{.name = "iso14a", .value = NfcProtocolIso14443_3a},
|
||||
|
||||
{.name = "14b", .value = NfcProtocolIso14443_3b},
|
||||
{.name = "iso14b", .value = NfcProtocolIso14443_3b},
|
||||
|
||||
{.name = "15", .value = NfcProtocolIso15693_3},
|
||||
{.name = "felica", .value = NfcProtocolFelica},
|
||||
};
|
||||
|
||||
static bool nfc_cli_raw_parse_protocol(FuriString* value, void* output) {
|
||||
NfcCliRawCmdContext* ctx = output;
|
||||
NfcProtocol new_protocol = NfcProtocolInvalid;
|
||||
|
||||
NfcCliProtocolParser* parser =
|
||||
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
|
||||
|
||||
bool result = nfc_cli_protocol_parser_get(parser, value, &new_protocol);
|
||||
|
||||
nfc_cli_protocol_parser_free(parser);
|
||||
|
||||
if(result && ctx->request.protocol != NfcProtocolInvalid &&
|
||||
ctx->request.protocol != new_protocol) {
|
||||
printf(
|
||||
ANSI_FG_RED "Error: previous %s != new %s. Unable to continue." ANSI_RESET,
|
||||
nfc_cli_get_protocol_name(ctx->request.protocol),
|
||||
nfc_cli_get_protocol_name(new_protocol));
|
||||
result = false;
|
||||
}
|
||||
|
||||
if(result) {
|
||||
ctx->request.protocol = new_protocol;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool nfc_cli_raw_parse_data(FuriString* value, void* output) {
|
||||
NfcCliRawCmdContext* ctx = output;
|
||||
|
||||
bool result = false;
|
||||
do {
|
||||
size_t len = furi_string_size(value);
|
||||
if(len % 2 != 0) break;
|
||||
|
||||
size_t data_length = len / 2;
|
||||
uint8_t* data = malloc(data_length);
|
||||
|
||||
if(args_read_hex_bytes(value, data, data_length)) {
|
||||
bit_buffer_reset(ctx->request.tx_buffer);
|
||||
bit_buffer_copy_bytes(ctx->request.tx_buffer, data, data_length);
|
||||
result = true;
|
||||
}
|
||||
|
||||
free(data);
|
||||
} while(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool nfc_cli_raw_parse_timeout(FuriString* value, void* output) {
|
||||
furi_assert(value);
|
||||
furi_assert(output);
|
||||
NfcCliRawCmdContext* ctx = output;
|
||||
|
||||
bool result = false;
|
||||
|
||||
int timeout = 0;
|
||||
if(args_read_int_and_trim(value, &timeout)) {
|
||||
ctx->request.timeout = timeout;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool nfc_cli_raw_parse_select(FuriString* value, void* output) {
|
||||
UNUSED(value);
|
||||
NfcCliRawCmdContext* ctx = output;
|
||||
ctx->request.select = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nfc_cli_raw_parse_crc(FuriString* value, void* output) {
|
||||
UNUSED(value);
|
||||
NfcCliRawCmdContext* ctx = output;
|
||||
ctx->request.append_crc = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nfc_cli_raw_parse_keep(FuriString* value, void* output) {
|
||||
UNUSED(value);
|
||||
NfcCliRawCmdContext* ctx = output;
|
||||
ctx->request.keep_field = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const NfcCliKeyDescriptor raw_action_keys[] = {
|
||||
{
|
||||
.long_name = NULL,
|
||||
.short_name = "t",
|
||||
.features = {.parameter = true, .required = false},
|
||||
.description = "timeout in fc",
|
||||
.parse = nfc_cli_raw_parse_timeout,
|
||||
},
|
||||
{
|
||||
.long_name = NULL,
|
||||
.short_name = "k",
|
||||
.description = "keep signal field ON after receive",
|
||||
.parse = nfc_cli_raw_parse_keep,
|
||||
},
|
||||
{
|
||||
.long_name = NULL,
|
||||
.short_name = "c",
|
||||
.description = "calculate and append CRC",
|
||||
.parse = nfc_cli_raw_parse_crc,
|
||||
},
|
||||
{
|
||||
.long_name = NULL,
|
||||
.short_name = "s",
|
||||
.description = "Select on FieldOn",
|
||||
.parse = nfc_cli_raw_parse_select,
|
||||
},
|
||||
{
|
||||
.long_name = "protocol",
|
||||
.short_name = "p",
|
||||
.description = "desired protocol. Possible values: 14a, iso14a, 14b, iso14b, 15, felica",
|
||||
.features = {.parameter = true, .required = true},
|
||||
.parse = nfc_cli_raw_parse_protocol,
|
||||
},
|
||||
{
|
||||
.long_name = "data",
|
||||
.short_name = "d",
|
||||
.description = "Raw bytes to send in HEX format",
|
||||
.features = {.parameter = true, .required = true},
|
||||
.parse = nfc_cli_raw_parse_data,
|
||||
},
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor raw_action = {
|
||||
.name = "raw",
|
||||
.description = "Sends raw bytes using different protocols",
|
||||
.key_count = COUNT_OF(raw_action_keys),
|
||||
.keys = raw_action_keys,
|
||||
.execute = nfc_cli_raw_execute,
|
||||
.alloc = nfc_cli_raw_alloc_ctx,
|
||||
.free = nfc_cli_raw_free_ctx,
|
||||
.can_reuse = nfc_cli_raw_can_reuse_ctx,
|
||||
};
|
||||
|
||||
const NfcCliActionDescriptor* raw_actions_collection[] = {&raw_action};
|
||||
|
||||
ADD_NFC_CLI_COMMAND(raw, "", raw_actions_collection);
|
||||
|
||||
//Command usage: raw <protocol> [keys] <data>
|
||||
//Command examples:
|
||||
//raw iso14a -sc 3000
|
||||
//raw iso14a 3000
|
||||
//raw iso14a 3000 -sc
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor raw_cmd;
|
||||
@@ -0,0 +1,101 @@
|
||||
#include "nfc_cli_raw_felica.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/helpers/felica_crc.h>
|
||||
#include <nfc/protocols/felica/felica.h>
|
||||
#include <nfc/protocols/felica/felica_poller_i.h>
|
||||
|
||||
#define TAG "FELICA"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static inline void felica_format_activation_data(const FelicaData* data, FuriString* output) {
|
||||
nfc_cli_format_array(data->idm.data, FELICA_IDM_SIZE, "IDm: ", output);
|
||||
nfc_cli_format_array(data->pmm.data, FELICA_PMM_SIZE, " PMm: ", output);
|
||||
}
|
||||
|
||||
static NfcCliRawError nfc_cli_raw_felica_process_error(FelicaError error) {
|
||||
switch(error) {
|
||||
case FelicaErrorNone:
|
||||
return NfcCliRawErrorNone;
|
||||
case FelicaErrorTimeout:
|
||||
return NfcCliRawErrorTimeout;
|
||||
case FelicaErrorWrongCrc:
|
||||
return NfcCliRawErrorWrongCrc;
|
||||
case FelicaErrorNotPresent:
|
||||
return NfcCliRawErrorNotPresent;
|
||||
default:
|
||||
return NfcCliRawErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
static FelicaError nfc_cli_raw_felica_poller_process_error(NfcError error) {
|
||||
switch(error) {
|
||||
case NfcErrorNone:
|
||||
return FelicaErrorNone;
|
||||
case NfcErrorTimeout:
|
||||
return FelicaErrorTimeout;
|
||||
default:
|
||||
return FelicaErrorNotPresent;
|
||||
}
|
||||
}
|
||||
|
||||
static inline NfcCliRawError
|
||||
nfc_cli_raw_felica_activate(NfcGenericInstance* poller, FuriString* activation_string) {
|
||||
FelicaData felica_data = {};
|
||||
FelicaPoller* felica_poller = poller;
|
||||
FURI_LOG_D(TAG, "Activating...");
|
||||
|
||||
FelicaError error = felica_poller_activate(felica_poller, &felica_data);
|
||||
if(error == FelicaErrorNone) {
|
||||
felica_format_activation_data(&felica_data, activation_string);
|
||||
}
|
||||
|
||||
return nfc_cli_raw_felica_process_error(error);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError nfc_cli_raw_felica_txrx(
|
||||
NfcGenericInstance* poller,
|
||||
BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t timeout) {
|
||||
FURI_LOG_D(TAG, "TxRx");
|
||||
FelicaPoller* felica_poller = poller;
|
||||
|
||||
bit_buffer_reset(rx_buffer);
|
||||
|
||||
FelicaError error = FelicaErrorNone;
|
||||
|
||||
NfcError nfc_error = nfc_poller_trx(felica_poller->nfc, tx_buffer, rx_buffer, timeout);
|
||||
if(nfc_error != NfcErrorNone) {
|
||||
error = nfc_cli_raw_felica_poller_process_error(nfc_error);
|
||||
} else if(!felica_crc_check(rx_buffer)) {
|
||||
error = FelicaErrorWrongCrc;
|
||||
}
|
||||
|
||||
return nfc_cli_raw_felica_process_error(error);
|
||||
}
|
||||
|
||||
NfcCommand nfc_cli_raw_felica_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response) {
|
||||
do {
|
||||
if(request->select) {
|
||||
response->result = nfc_cli_raw_felica_activate(poller, response->activation_string);
|
||||
}
|
||||
|
||||
if(response->result != NfcCliRawErrorNone) break;
|
||||
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
|
||||
|
||||
if(request->append_crc) {
|
||||
FURI_LOG_D(TAG, "Add CRC");
|
||||
felica_crc_append(request->tx_buffer);
|
||||
}
|
||||
|
||||
uint32_t timeout = request->timeout > 0 ? request->timeout : FELICA_FDT_POLL_FC;
|
||||
response->result =
|
||||
nfc_cli_raw_felica_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
|
||||
} while(false);
|
||||
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_raw_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_raw_felica_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response);
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
#include "nfc_cli_raw_iso14443_3a.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/helpers/iso14443_crc.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
|
||||
|
||||
#define TAG "ISO14A"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static NfcCliRawError nfc_cli_raw_iso14443_3a_process_error(Iso14443_3aError error) {
|
||||
switch(error) {
|
||||
case Iso14443_3aErrorNone:
|
||||
return NfcCliRawErrorNone;
|
||||
case Iso14443_3aErrorTimeout:
|
||||
return NfcCliRawErrorTimeout;
|
||||
case Iso14443_3aErrorWrongCrc:
|
||||
return NfcCliRawErrorWrongCrc;
|
||||
case Iso14443_3aErrorNotPresent:
|
||||
return NfcCliRawErrorNotPresent;
|
||||
default:
|
||||
return NfcCliRawErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
static void iso14443_3a_format_activation_data(const Iso14443_3aData* data, FuriString* output) {
|
||||
nfc_cli_format_array(data->uid, data->uid_len, "UID: ", output);
|
||||
furi_string_cat_printf(
|
||||
output, " ATQA: %02X%02X SAK: %02X", data->atqa[0], data->atqa[1], data->sak);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError
|
||||
nfc_cli_raw_iso14443_3a_activate(NfcGenericInstance* poller, FuriString* activation_string) {
|
||||
Iso14443_3aData iso3_data = {};
|
||||
FURI_LOG_D(TAG, "Activating...");
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_activate(poller, &iso3_data);
|
||||
if(error == Iso14443_3aErrorNone)
|
||||
iso14443_3a_format_activation_data(&iso3_data, activation_string);
|
||||
|
||||
return nfc_cli_raw_iso14443_3a_process_error(error);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError nfc_cli_raw_iso14443_3a_txrx(
|
||||
NfcGenericInstance* poller,
|
||||
BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t timeout) {
|
||||
FURI_LOG_D(TAG, "TxRx");
|
||||
bit_buffer_reset(rx_buffer);
|
||||
Iso14443_3aError error = iso14443_3a_poller_txrx(poller, tx_buffer, rx_buffer, timeout);
|
||||
return nfc_cli_raw_iso14443_3a_process_error(error);
|
||||
}
|
||||
|
||||
NfcCommand nfc_cli_raw_iso14443_3a_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response) {
|
||||
do {
|
||||
response->result = NfcCliRawErrorNone;
|
||||
if(request->select) {
|
||||
response->result =
|
||||
nfc_cli_raw_iso14443_3a_activate(poller, response->activation_string);
|
||||
}
|
||||
|
||||
if(response->result != NfcCliRawErrorNone) break;
|
||||
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
|
||||
|
||||
if(request->append_crc) {
|
||||
FURI_LOG_D(TAG, "Add CRC");
|
||||
iso14443_crc_append(Iso14443CrcTypeA, request->tx_buffer);
|
||||
}
|
||||
|
||||
uint32_t timeout = request->timeout > 0 ? request->timeout : ISO14443_3A_FDT_LISTEN_FC;
|
||||
response->result =
|
||||
nfc_cli_raw_iso14443_3a_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
|
||||
} while(false);
|
||||
|
||||
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_raw_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_raw_iso14443_3a_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response);
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
#include "nfc_cli_raw_iso14443_3b.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/helpers/iso14443_crc.h>
|
||||
#include <nfc/protocols/iso14443_3b/iso14443_3b_i.h>
|
||||
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h>
|
||||
|
||||
#define TAG "ISO14B"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static NfcCliRawError nfc_cli_raw_iso14443_3b_process_error(Iso14443_3bError error) {
|
||||
switch(error) {
|
||||
case Iso14443_3bErrorNone:
|
||||
return NfcCliRawErrorNone;
|
||||
case Iso14443_3bErrorTimeout:
|
||||
return NfcCliRawErrorTimeout;
|
||||
case Iso14443_3bErrorWrongCrc:
|
||||
return NfcCliRawErrorWrongCrc;
|
||||
case Iso14443_3bErrorNotPresent:
|
||||
return NfcCliRawErrorNotPresent;
|
||||
default:
|
||||
return NfcCliRawErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
static Iso14443_3bError nfc_cli_raw_iso14443_3b_poller_process_error(NfcError error) {
|
||||
switch(error) {
|
||||
case NfcErrorNone:
|
||||
return Iso14443_3bErrorNone;
|
||||
case NfcErrorTimeout:
|
||||
return Iso14443_3bErrorTimeout;
|
||||
default:
|
||||
return Iso14443_3bErrorNotPresent;
|
||||
}
|
||||
}
|
||||
|
||||
static void iso14443_3b_format_activation_data(const Iso14443_3bData* data, FuriString* output) {
|
||||
nfc_cli_format_array(data->uid, ISO14443_3B_UID_SIZE, "UID: ", output);
|
||||
|
||||
const Iso14443_3bProtocolInfo* info = &data->protocol_info;
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
" BitRate: %d, Protocol: %d, Max Frame Size: %d, Fo: %d, Adc: %d, Fwi: %d",
|
||||
info->bit_rate_capability,
|
||||
info->protocol_type,
|
||||
info->max_frame_size,
|
||||
info->fo,
|
||||
info->adc,
|
||||
info->fwi);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError nfc_cli_raw_iso14443_3b_activate(
|
||||
NfcGenericInstance* poller,
|
||||
Iso14443_3bData* iso3b_data,
|
||||
FuriString* activation_string) {
|
||||
FURI_LOG_D(TAG, "Activating...");
|
||||
|
||||
Iso14443_3bError error = iso14443_3b_poller_activate(poller, iso3b_data);
|
||||
if(error == Iso14443_3bErrorNone)
|
||||
iso14443_3b_format_activation_data(iso3b_data, activation_string);
|
||||
|
||||
return nfc_cli_raw_iso14443_3b_process_error(error);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError nfc_cli_raw_iso14443_3b_txrx(
|
||||
NfcGenericInstance* poller,
|
||||
BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t timeout) {
|
||||
FURI_LOG_D(TAG, "TxRx");
|
||||
Iso14443_3bPoller* iso14b_poller = poller;
|
||||
|
||||
bit_buffer_reset(rx_buffer);
|
||||
|
||||
Iso14443_3bError error = Iso14443_3bErrorNone;
|
||||
|
||||
NfcError nfc_error = nfc_poller_trx(iso14b_poller->nfc, tx_buffer, rx_buffer, timeout);
|
||||
if(nfc_error != NfcErrorNone) {
|
||||
error = nfc_cli_raw_iso14443_3b_poller_process_error(nfc_error);
|
||||
} else if(!iso14443_crc_check(Iso14443CrcTypeB, rx_buffer)) {
|
||||
error = Iso14443_3bErrorWrongCrc;
|
||||
}
|
||||
|
||||
return nfc_cli_raw_iso14443_3b_process_error(error);
|
||||
}
|
||||
|
||||
NfcCommand nfc_cli_raw_iso14443_3b_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response) {
|
||||
Iso14443_3bData iso3b_data = {0};
|
||||
bool activated = false;
|
||||
do {
|
||||
response->result = NfcCliRawErrorNone;
|
||||
if(request->select) {
|
||||
response->result =
|
||||
nfc_cli_raw_iso14443_3b_activate(poller, &iso3b_data, response->activation_string);
|
||||
activated = response->result == NfcCliRawErrorNone;
|
||||
}
|
||||
|
||||
if(response->result != NfcCliRawErrorNone) break;
|
||||
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
|
||||
|
||||
uint32_t timeout = ISO14443_3B_FDT_POLL_FC;
|
||||
if(request->timeout > 0) {
|
||||
timeout = request->timeout;
|
||||
} else if(activated) {
|
||||
timeout = iso14443_3b_get_fwt_fc_max(&iso3b_data);
|
||||
}
|
||||
|
||||
if(request->append_crc) {
|
||||
FURI_LOG_D(TAG, "Add CRC");
|
||||
iso14443_crc_append(Iso14443CrcTypeB, request->tx_buffer);
|
||||
}
|
||||
|
||||
response->result =
|
||||
nfc_cli_raw_iso14443_3b_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
|
||||
} while(false);
|
||||
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_raw_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_raw_iso14443_3b_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response);
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
#include "nfc_cli_raw_iso15693_3.h"
|
||||
#include "../../../helpers/nfc_cli_format.h"
|
||||
|
||||
#include <nfc/helpers/iso13239_crc.h>
|
||||
#include <nfc/protocols/iso15693_3/iso15693_3.h>
|
||||
#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>
|
||||
|
||||
#define TAG "ISO15"
|
||||
|
||||
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
|
||||
|
||||
static NfcCliRawError nfc_cli_raw_iso15693_3_process_error(Iso15693_3Error error) {
|
||||
switch(error) {
|
||||
case Iso15693_3ErrorNone:
|
||||
return NfcCliRawErrorNone;
|
||||
case Iso15693_3ErrorTimeout:
|
||||
return NfcCliRawErrorTimeout;
|
||||
case Iso15693_3ErrorWrongCrc:
|
||||
return NfcCliRawErrorWrongCrc;
|
||||
case Iso15693_3ErrorNotPresent:
|
||||
return NfcCliRawErrorNotPresent;
|
||||
default:
|
||||
return NfcCliRawErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
static Iso15693_3Error nfc_cli_raw_iso15693_3_poller_process_nfc_error(NfcError error) {
|
||||
switch(error) {
|
||||
case NfcErrorNone:
|
||||
return Iso15693_3ErrorNone;
|
||||
case NfcErrorTimeout:
|
||||
return Iso15693_3ErrorTimeout;
|
||||
default:
|
||||
return Iso15693_3ErrorNotPresent;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void iso15693_3_format_activation_data(const uint8_t* data, FuriString* output) {
|
||||
nfc_cli_format_array(data, ISO15693_3_UID_SIZE, "UID: ", output);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError
|
||||
nfc_cli_raw_iso15693_3_activate(NfcGenericInstance* poller, FuriString* activation_string) {
|
||||
FURI_LOG_D(TAG, "Activating...");
|
||||
|
||||
Iso15693_3Poller* iso15_poller = poller;
|
||||
uint8_t uid[ISO15693_3_UID_SIZE] = {0};
|
||||
|
||||
Iso15693_3Error error = iso15693_3_poller_inventory(iso15_poller, uid);
|
||||
if(error == Iso15693_3ErrorNone) {
|
||||
iso15693_3_format_activation_data(uid, activation_string);
|
||||
}
|
||||
return nfc_cli_raw_iso15693_3_process_error(error);
|
||||
}
|
||||
|
||||
static inline NfcCliRawError nfc_cli_raw_iso15693_3_txrx(
|
||||
NfcGenericInstance* poller,
|
||||
BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t timeout) {
|
||||
FURI_LOG_D(TAG, "TxRx");
|
||||
|
||||
Iso15693_3Poller* iso15_poller = poller;
|
||||
|
||||
bit_buffer_reset(rx_buffer);
|
||||
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
NfcError nfc_error = nfc_poller_trx(iso15_poller->nfc, tx_buffer, rx_buffer, timeout);
|
||||
if(nfc_error != NfcErrorNone) {
|
||||
error = nfc_cli_raw_iso15693_3_poller_process_nfc_error(nfc_error);
|
||||
} else if(!iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
|
||||
error = Iso15693_3ErrorWrongCrc;
|
||||
}
|
||||
|
||||
return nfc_cli_raw_iso15693_3_process_error(error);
|
||||
}
|
||||
|
||||
NfcCommand nfc_cli_raw_iso15693_3_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response) {
|
||||
do {
|
||||
if(request->select) {
|
||||
response->result =
|
||||
nfc_cli_raw_iso15693_3_activate(poller, response->activation_string);
|
||||
}
|
||||
|
||||
if(response->result != NfcCliRawErrorNone) break;
|
||||
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
|
||||
|
||||
if(request->append_crc) {
|
||||
FURI_LOG_D(TAG, "Add CRC");
|
||||
iso13239_crc_append(Iso13239CrcTypeDefault, request->tx_buffer);
|
||||
}
|
||||
|
||||
uint32_t timeout = request->timeout > 0 ? request->timeout : ISO15693_3_FDT_POLL_FC;
|
||||
response->result =
|
||||
nfc_cli_raw_iso15693_3_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
|
||||
} while(false);
|
||||
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_raw_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_raw_iso15693_3_handler(
|
||||
NfcGenericInstance* poller,
|
||||
const NfcCliRawRequest* request,
|
||||
NfcCliRawResponse* const response);
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <nfc/nfc.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
typedef enum {
|
||||
NfcCliRawErrorNone,
|
||||
NfcCliRawErrorTimeout,
|
||||
NfcCliRawErrorNotPresent,
|
||||
NfcCliRawErrorWrongCrc,
|
||||
NfcCliRawErrorProtocol,
|
||||
} NfcCliRawError;
|
||||
|
||||
typedef struct {
|
||||
bool select;
|
||||
bool keep_field;
|
||||
bool append_crc;
|
||||
NfcProtocol protocol;
|
||||
BitBuffer* tx_buffer;
|
||||
uint32_t timeout;
|
||||
} NfcCliRawRequest;
|
||||
|
||||
typedef struct {
|
||||
NfcCliRawError result;
|
||||
BitBuffer* rx_buffer;
|
||||
FuriString* activation_string;
|
||||
} NfcCliRawResponse;
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "nfc_cli_commands.h"
|
||||
#include "nfc_cli_command_processor.h"
|
||||
|
||||
#include "applications/services/loader/loader.h"
|
||||
#include "applications/services/cli/cli_main_commands.h"
|
||||
#include <toolbox/cli/shell/cli_shell.h>
|
||||
#include <toolbox/cli/cli_registry.h>
|
||||
|
||||
#define NFC_DESKTOP_APP_NAME "NFC"
|
||||
|
||||
#define TAG "NfcCli"
|
||||
|
||||
#define NFC_PROMPT "[" ANSI_FG_GREEN "nfc" ANSI_RESET "]"
|
||||
|
||||
typedef struct {
|
||||
Nfc* nfc;
|
||||
CliRegistry* registry;
|
||||
CliShell* shell;
|
||||
NfcCliProcessorContext* processor_context;
|
||||
} NfcCliContext;
|
||||
|
||||
static void nfc_cli_shell_motd(void* context) {
|
||||
UNUSED(context);
|
||||
printf(ANSI_FG_BR_BLUE "\r\n"
|
||||
" 0000 \r\n"
|
||||
" 0000 \r\n"
|
||||
" 000 0000 \r\n"
|
||||
" 0000 00000 \r\n"
|
||||
" 000 00000 0000 \r\n"
|
||||
" 0 0000 0000 00000 \r\n"
|
||||
" 000000 0000 00000 0000 \r\n"
|
||||
" 00000000 0000 0000 0000 \r\n"
|
||||
" 0000000000 0000 00000 0000 \r\n"
|
||||
" 0000 00000000 00000 00000 0000 \r\n"
|
||||
" 0000 0000000 00000 00000 0000 \r\n"
|
||||
" 0000 000000000000 0000 0000 \r\n"
|
||||
" 00000 000000000 00000 0000 \r\n"
|
||||
" 00 000000 0000 00000 \r\n"
|
||||
" 00 00000 0000 \r\n"
|
||||
" 0000 00000 \r\n"
|
||||
" 000 0000 \r\n"
|
||||
" 0000 \r\n"
|
||||
" 0005 \r\n"
|
||||
"\r\n" ANSI_FG_BR_WHITE "Welcome to NFC Command Line Interface!\r\n"
|
||||
"Run `help` or `?` to list available commands\r\n" ANSI_RESET);
|
||||
}
|
||||
|
||||
static void nfc_cli_subscribe_commands(NfcCliContext* instance) {
|
||||
size_t cnt = nfc_cli_command_get_count();
|
||||
for(size_t i = 0; i < cnt; i++) {
|
||||
const NfcCliCommandDescriptor* cmd = nfc_cli_command_get_by_index(i);
|
||||
CliCommandExecuteCallback callback = nfc_cli_command_get_execute(cmd);
|
||||
if(callback == NULL) continue;
|
||||
const char* name = nfc_cli_command_get_name(cmd);
|
||||
cli_registry_add_command(
|
||||
instance->registry,
|
||||
name,
|
||||
CliCommandFlagParallelSafe,
|
||||
callback,
|
||||
instance->processor_context);
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_cli_desktop_app_is_running() {
|
||||
FuriString* app_name = furi_string_alloc();
|
||||
Loader* ldr = furi_record_open(RECORD_LOADER);
|
||||
bool result = false;
|
||||
|
||||
if(loader_get_application_name(ldr, app_name)) {
|
||||
result = furi_string_equal_str(app_name, NFC_DESKTOP_APP_NAME);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_LOADER);
|
||||
furi_string_free(app_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static NfcCliContext* nfc_cli_alloc(PipeSide* pipe) {
|
||||
NfcCliContext* instance = malloc(sizeof(NfcCliContext));
|
||||
instance->nfc = nfc_alloc();
|
||||
instance->processor_context = nfc_cli_command_processor_alloc(instance->nfc);
|
||||
|
||||
instance->registry = cli_registry_alloc();
|
||||
|
||||
nfc_cli_subscribe_commands(instance);
|
||||
|
||||
instance->shell =
|
||||
cli_shell_alloc(nfc_cli_shell_motd, instance, pipe, instance->registry, NULL);
|
||||
|
||||
cli_shell_set_prompt(instance->shell, NFC_PROMPT);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_free(NfcCliContext* instance) {
|
||||
furi_assert(instance);
|
||||
nfc_cli_command_processor_free(instance->processor_context);
|
||||
|
||||
cli_shell_free(instance->shell);
|
||||
cli_registry_free(instance->registry);
|
||||
|
||||
nfc_free(instance->nfc);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_cli_execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
furi_assert(pipe);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
if(nfc_cli_desktop_app_is_running()) {
|
||||
printf(ANSI_FG_YELLOW
|
||||
"NFC app is running, unable to run NFC CLI at the same time!\r\n" ANSI_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
NfcCliContext* instance = nfc_cli_alloc(pipe);
|
||||
|
||||
cli_shell_start(instance->shell);
|
||||
cli_shell_join(instance->shell);
|
||||
|
||||
nfc_cli_free(instance);
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(nfc, nfc_cli_execute, CliCommandFlagParallelSafe, 1024, CLI_APPID);
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <nfc/nfc.h>
|
||||
|
||||
/**
|
||||
* @brief Type for action context to be created before action execution
|
||||
* must be hanlded through callbacks in each action separately
|
||||
*/
|
||||
typedef void NfcCliActionContext;
|
||||
|
||||
/**
|
||||
* @brief Callback type for function of action context allocation
|
||||
* @param nfc Instance of NFC subsystem, will be used during action execution
|
||||
* @return Pointer to action context
|
||||
*/
|
||||
typedef NfcCliActionContext* (*NfcCliActionContextAlloc)(Nfc* nfc);
|
||||
|
||||
/**
|
||||
* @brief Callback for action context deleting
|
||||
* @param action_ctx Action context to be freed
|
||||
*/
|
||||
typedef void (*NfcCliActionContextFree)(NfcCliActionContext* action_ctx);
|
||||
|
||||
/**
|
||||
* @brief Callback invoked by command processor to determine whether already
|
||||
* existing context (from previously executed command) can be reused for the new one.
|
||||
*
|
||||
* @param action_ctx Action context
|
||||
*
|
||||
* In most cases re-creating of a new context is not needed.
|
||||
* It is used in 'raw' command, where nfc field sometimes need to stay turned on between
|
||||
* commands.
|
||||
*
|
||||
* Handling of this situation and decision about reusing action context is on developer,
|
||||
* who need to decide, can this command reuse context.
|
||||
*
|
||||
* It can be done by comparing parameters of previously executed command and a new one,
|
||||
* or by some args in command.
|
||||
*
|
||||
* See implementation of 'keep_field' flag in 'raw' command for example.
|
||||
*/
|
||||
typedef bool (*NfcCliActionContextCanReuse)(NfcCliActionContext* ctx);
|
||||
|
||||
/**
|
||||
* @brief Action execution callback
|
||||
* @param pipe provided by cli shell, can be used for command termination
|
||||
* @param ctx Action context
|
||||
*/
|
||||
typedef void (*NfcCliActionHandlerCallback)(PipeSide* pipe, NfcCliActionContext* ctx);
|
||||
|
||||
/**
|
||||
* @brief Callback used for parsing argument key.
|
||||
* Each key added to command must have this, otherwise parsing result will always be
|
||||
* false and command will never be executed.
|
||||
*
|
||||
* @param value Text value for the key to be parsed
|
||||
* @param ctx Action context
|
||||
* @return true when parsing was fine, otherwise false. If any argument in command input
|
||||
* generates false during parsing, command will not be executed and error will be shown
|
||||
*/
|
||||
typedef bool (*NfcCliArgParseCallback)(FuriString* value, NfcCliActionContext* ctx);
|
||||
|
||||
typedef struct NfcCliKeyDescriptor NfcCliKeyDescriptor;
|
||||
|
||||
typedef struct NfcCliActionDescriptor NfcCliActionDescriptor;
|
||||
|
||||
typedef struct NfcCliCommandDescriptor NfcCliCommandDescriptor;
|
||||
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include "nfc_cli_command_base.h"
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
#include <nfc/nfc.h>
|
||||
#include <nfc/protocols/nfc_protocol.h>
|
||||
#include "nfc_cli_command_processor.h"
|
||||
|
||||
/**
|
||||
* @brief How to add command.
|
||||
*
|
||||
* There are 3 possible option on how to add new command to nfc_cli:
|
||||
*
|
||||
* @see Option 1 "Add action command directly to nfc_shell"
|
||||
*
|
||||
* In this case command will be invoked with argument string from nfc_shell.
|
||||
* Command registration must be performed directly by user.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Add new function for command to nfc_cli.c
|
||||
* 2. In nfc_cli_alloc function register command using cli_registry_add_command after nfc_cli_subscribe_commands
|
||||
*
|
||||
* This option is NOT RECOMENDED, because such command will not have any 'help'
|
||||
* processing and parsing error checks. Argument parsing must also be done by hand.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* @see Option 2 "Add action command to collection without further processing"
|
||||
*
|
||||
* In this case command will be invoked with argument string from nfc_shell.
|
||||
* nfc_cli_command_processor is skipped, so argument handling is up to the developer.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Add new pair of nfc_cli_command_<cmd>.c/.h files to /commands folder
|
||||
* 2. Define const NfcCliCommandDescriptor instance in .c file and its extern definition in .h file
|
||||
* 3. Include .h file to nfc_cli_commands.c file below comment "Include new commands here"
|
||||
* 4. Add new command reference to nfc_cli_commands array
|
||||
* 5. Add path to nfc_cli_command_<cmd>.c file into 'cli_nfc' plugin in application.fam file
|
||||
*
|
||||
* This option suites for simple commands with no any parameters.
|
||||
* @see nfc_cli_command_field.c implementation as an example.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* @see Option 3 "Add action command to collection with full processing"
|
||||
*
|
||||
* In this nfc_cli_command_processor will be invoked for parsing command arguments
|
||||
* and action execution. Also it will handle errors and help printing.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Add new pair of nfc_cli_command_<cmd>.c/.h files to /commands folder
|
||||
* 2. Use macro ADD_NFC_CLI_COMMAND to define command in .c file
|
||||
* 3. Define command extern in .h file, using command name from macro in form of "<name>_cmd"
|
||||
* 4. Add all desired actions and keys to your command
|
||||
* 5. Include .h file to nfc_cli_commands.c file below comment "Include new commands here"
|
||||
* 6. Add new command reference to nfc_cli_commands array
|
||||
* 7. Add path to nfc_cli_command_<cmd>.c file into 'cli_nfc' plugin in application.fam file
|
||||
*
|
||||
* This option suites for "difficult" commands which has actions with lots of keys.
|
||||
* @see nfc_cli_command_emulate.c implementation as an example.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Used to decorate argument with some properties
|
||||
*/
|
||||
typedef struct {
|
||||
bool required : 1; /**< Command always needs this argument. Missing arguments with this set to true will result execution error.*/
|
||||
bool parameter : 1; /**< Such argument requires value after its name, otherwise it is a simple on/off switch */
|
||||
bool multivalue : 1; /**< Such argument can take multiple values after its name, like this "-key value1 value2 .. valueN" */
|
||||
} FURI_PACKED NfcCliKeyFeatureSupport;
|
||||
|
||||
/**
|
||||
* @brief Describes key for action
|
||||
*/
|
||||
struct NfcCliKeyDescriptor {
|
||||
NfcCliKeyFeatureSupport features; /**< Features supported defining key behaviour */
|
||||
const char* long_name; /**< Long key name starts with '--' symbol in argument string */
|
||||
const char* short_name; /**< Short key name starts with '-' symbol in argument string */
|
||||
const char* description; /**< Key description showed in help */
|
||||
NfcCliArgParseCallback parse; /**< Parsing callback */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Describes action
|
||||
*/
|
||||
struct NfcCliActionDescriptor {
|
||||
const char* name; /**< Action name MUST be the first argument after command.*/
|
||||
const char* description; /**< Description showed in help */
|
||||
size_t key_count; /**< Amount of key entries in keys array */
|
||||
const NfcCliKeyDescriptor* keys; /**< Keys available for action */
|
||||
|
||||
NfcCliActionHandlerCallback execute; /**< Action callback, invoked if parsing is ok */
|
||||
NfcCliActionContextAlloc alloc; /**< Allocates action context during command processing */
|
||||
NfcCliActionContextFree free; /**< Frees action context */
|
||||
NfcCliActionContextCanReuse can_reuse; /**< Checks context reuse possibility */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Describes command
|
||||
*/
|
||||
struct NfcCliCommandDescriptor {
|
||||
const char* name; /** Used to register command in cli shell */
|
||||
const char* description; /**< Description showed in help */
|
||||
size_t action_count; /** Amount of actions available in scope of this particular command */
|
||||
const NfcCliActionDescriptor** actions; /**< Actions available for command */
|
||||
CliCommandExecuteCallback callback; /** Entry point for command */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This macro simplifies command creation. It fills instance of
|
||||
* NfcCliCommandDescriptor and generates a callback which invokes
|
||||
* nfc_cli_command_processor inside
|
||||
*/
|
||||
#define ADD_NFC_CLI_COMMAND(name, description, actions) \
|
||||
static void nfc_cli_command_##name##_callback( \
|
||||
PipeSide* pipe, FuriString* args, void* context); \
|
||||
\
|
||||
const NfcCliCommandDescriptor name##_cmd = { \
|
||||
#name, \
|
||||
#description, \
|
||||
COUNT_OF(actions), \
|
||||
actions, \
|
||||
nfc_cli_command_##name##_callback, \
|
||||
}; \
|
||||
\
|
||||
static void nfc_cli_command_##name##_callback( \
|
||||
PipeSide* pipe, FuriString* args, void* context) { \
|
||||
nfc_cli_command_processor_run(&name##_cmd, pipe, args, context); \
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
#include "nfc_cli_command_processor.h"
|
||||
#include "nfc_cli_commands.h"
|
||||
#include "nfc_cli_command_base_i.h"
|
||||
|
||||
#include <m-string.h>
|
||||
#include <args.h>
|
||||
#include <hex.h>
|
||||
|
||||
#define TAG "NfcCliProcessor"
|
||||
|
||||
#define NFC_CLI_KEYS_FOUND_SIZE_BYTES (10 * sizeof(NfcCliKeyDescriptor*))
|
||||
|
||||
typedef enum {
|
||||
NfcCliArgumentTypeShortNameKey,
|
||||
NfcCliArgumentTypeShortNameKeyGroup,
|
||||
NfcCliArgumentTypeLongNameKey,
|
||||
|
||||
NfcCliArgumentTypeUnknown
|
||||
} NfcCliArgumentType;
|
||||
|
||||
/**
|
||||
* @brief Error codes for different processing states
|
||||
*/
|
||||
typedef enum {
|
||||
NfcCliProcessorErrorNone, /**< Command was parsed successfully and execute callback will be invoked*/
|
||||
NfcCliProcessorErrorNoneButHelp, /**< There was no error, but help needs to be printed. Command wil not be executed */
|
||||
NfcCliProcessorErrorActionNotFound, /**< Wrong action was passed as first command parameter */
|
||||
NfcCliProcessorErrorKeyNotSupported, /**< Unsupported key was passed in arguments. Details will be printed in erro_message*/
|
||||
NfcCliProcessorErrorKeyParameterInGroup, /**< Parameter which requires value was passed in group. Example: -sckd */
|
||||
NfcCliProcessorErrorKeyParameterValueMissing, /**< Value is missing for the parameter which requires it */
|
||||
NfcCliProcessorErrorKeyDuplication, /**< Some argument key was duplicated in input parameters */
|
||||
NfcCliProcessorErrorKeyParseError, /**< Error happened during argument value parsing */
|
||||
NfcCliProcessorErrorKeyRequiredMissing, /**< Some keys required for command execution is missing*/
|
||||
|
||||
NfcCliProcessorErrorNum
|
||||
} NfcCliProcessorError;
|
||||
|
||||
struct NfcCliProcessorContext {
|
||||
const NfcCliCommandDescriptor* cmd;
|
||||
const NfcCliActionDescriptor* action;
|
||||
const NfcCliKeyDescriptor** keys_found;
|
||||
uint8_t total_keys_found;
|
||||
uint8_t required_keys_expected;
|
||||
uint8_t required_keys_found;
|
||||
|
||||
Nfc* nfc;
|
||||
void* action_context;
|
||||
|
||||
FuriString* error_message;
|
||||
};
|
||||
|
||||
static const NfcCliActionDescriptor*
|
||||
nfc_cli_get_action_from_args(const NfcCliCommandDescriptor* cmd, FuriString* args) {
|
||||
const NfcCliActionDescriptor* action = cmd->actions[0];
|
||||
|
||||
bool multiple_action_cmd = nfc_cli_command_has_multiple_actions(cmd);
|
||||
if(multiple_action_cmd) {
|
||||
action = NULL;
|
||||
FuriString* arg_str = furi_string_alloc();
|
||||
if(args_read_string_and_trim(args, arg_str)) {
|
||||
action = nfc_cli_command_get_action_by_name(cmd, arg_str);
|
||||
}
|
||||
furi_string_free(arg_str);
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
static bool nfc_cli_action_can_reuse_context(
|
||||
NfcCliProcessorContext* instance,
|
||||
const NfcCliActionDescriptor* new_action) {
|
||||
bool result = false;
|
||||
do {
|
||||
if(instance->action != new_action) break;
|
||||
if(new_action->can_reuse == NULL) break;
|
||||
result = new_action->can_reuse(instance->action_context);
|
||||
} while(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void nfc_cli_action_free(NfcCliProcessorContext* instance) {
|
||||
if(instance->action && instance->action->free) {
|
||||
FURI_LOG_D(TAG, "Free previous \"%s\" action context", instance->action->name);
|
||||
instance->action->free(instance->action_context);
|
||||
}
|
||||
instance->action = NULL;
|
||||
}
|
||||
|
||||
static NfcCliProcessorError
|
||||
nfc_cli_action_alloc(NfcCliProcessorContext* instance, FuriString* args) {
|
||||
const NfcCliCommandDescriptor* cmd = instance->cmd;
|
||||
|
||||
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
||||
do {
|
||||
const NfcCliActionDescriptor* action = nfc_cli_get_action_from_args(cmd, args);
|
||||
if(action == NULL) {
|
||||
result = NfcCliProcessorErrorActionNotFound;
|
||||
furi_string_printf(instance->error_message, "Action not found");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!nfc_cli_action_can_reuse_context(instance, action)) {
|
||||
nfc_cli_action_free(instance);
|
||||
|
||||
instance->action = action;
|
||||
if(action->alloc && action->free) {
|
||||
FURI_LOG_D(TAG, "Allocating context for action \"%s\"", action->name);
|
||||
instance->action_context = instance->action->alloc(instance->nfc);
|
||||
} else if(action->alloc && (action->free == NULL)) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Free callback not defined for action \"%s\". Skip allocation to avoid memory leak.",
|
||||
action->name);
|
||||
instance->action_context = NULL;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "No alloc context callback for action \"%s\"", action->name);
|
||||
instance->action_context = NULL;
|
||||
}
|
||||
} else
|
||||
FURI_LOG_D(TAG, "Reusing context from previous \"%s\" action", action->name);
|
||||
|
||||
memset(instance->keys_found, 0, NFC_CLI_KEYS_FOUND_SIZE_BYTES);
|
||||
instance->required_keys_expected = nfc_cli_action_get_required_keys_count(action);
|
||||
instance->required_keys_found = 0;
|
||||
instance->total_keys_found = 0;
|
||||
} while(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static NfcCliArgumentType nfc_cli_get_argument_type(FuriString* argument) {
|
||||
size_t arg_len = furi_string_size(argument);
|
||||
NfcCliArgumentType type = NfcCliArgumentTypeUnknown;
|
||||
|
||||
if(arg_len > 2) {
|
||||
char ch1 = furi_string_get_char(argument, 0);
|
||||
char ch2 = furi_string_get_char(argument, 1);
|
||||
if(ch1 == '-') {
|
||||
type = (ch2 == '-') ? NfcCliArgumentTypeLongNameKey :
|
||||
NfcCliArgumentTypeShortNameKeyGroup;
|
||||
}
|
||||
} else if(arg_len == 2) {
|
||||
char ch1 = furi_string_get_char(argument, 0);
|
||||
type = (ch1 == '-') ? NfcCliArgumentTypeShortNameKey : NfcCliArgumentTypeUnknown;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static bool
|
||||
nfc_cli_check_duplicate_keys(NfcCliProcessorContext* instance, const NfcCliKeyDescriptor* key) {
|
||||
bool result = false;
|
||||
for(size_t i = 0; i < instance->total_keys_found; i++) {
|
||||
const NfcCliKeyDescriptor* buf = instance->keys_found[i];
|
||||
if(buf != key) continue;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void nfc_cli_trim_multivalue_arg(FuriString* args, FuriString* value) {
|
||||
furi_string_set(value, args);
|
||||
size_t index = furi_string_search_char(value, '-', 0);
|
||||
if(index != STRING_FAILURE) {
|
||||
furi_string_left(value, index);
|
||||
furi_string_right(args, index);
|
||||
} else {
|
||||
furi_string_reset(args);
|
||||
}
|
||||
}
|
||||
|
||||
static NfcCliProcessorError nfc_cli_parse_single_key(
|
||||
NfcCliProcessorContext* instance,
|
||||
FuriString* argument,
|
||||
FuriString* args,
|
||||
bool from_group) {
|
||||
FuriString* value_str = furi_string_alloc();
|
||||
|
||||
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
||||
do {
|
||||
const NfcCliKeyDescriptor* key =
|
||||
nfc_cli_action_get_key_descriptor(instance->action, argument);
|
||||
if(key == NULL) {
|
||||
if(furi_string_equal_str(argument, "h"))
|
||||
result = NfcCliProcessorErrorNoneButHelp;
|
||||
else {
|
||||
furi_string_printf(
|
||||
instance->error_message,
|
||||
"Key \'%s\' is not supported",
|
||||
furi_string_get_cstr(argument));
|
||||
result = NfcCliProcessorErrorKeyNotSupported;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(key->features.parameter && from_group) {
|
||||
furi_string_printf(
|
||||
instance->error_message,
|
||||
"Parameter key \'%s\' can\'t be grouped",
|
||||
furi_string_get_cstr(argument));
|
||||
result = NfcCliProcessorErrorKeyParameterInGroup;
|
||||
break;
|
||||
}
|
||||
|
||||
if(nfc_cli_check_duplicate_keys(instance, key)) {
|
||||
furi_string_printf(
|
||||
instance->error_message, "Duplicated key \'%s\'", furi_string_get_cstr(argument));
|
||||
result = NfcCliProcessorErrorKeyDuplication;
|
||||
break;
|
||||
}
|
||||
|
||||
if(key->features.multivalue && !key->features.parameter) break;
|
||||
if(key->features.multivalue) {
|
||||
nfc_cli_trim_multivalue_arg(args, value_str);
|
||||
FURI_LOG_D(TAG, "Multivalue: %s", furi_string_get_cstr(value_str));
|
||||
} else if(key->features.parameter && !args_read_string_and_trim(args, value_str)) {
|
||||
result = NfcCliProcessorErrorKeyParameterValueMissing;
|
||||
furi_string_printf(
|
||||
instance->error_message,
|
||||
"Missing value for \'%s\'",
|
||||
furi_string_get_cstr(argument));
|
||||
break;
|
||||
}
|
||||
|
||||
if(key->parse == NULL) {
|
||||
furi_string_printf(
|
||||
instance->error_message,
|
||||
"Parse callback for key \'%s\' not defined",
|
||||
furi_string_get_cstr(argument));
|
||||
result = NfcCliProcessorErrorKeyParseError;
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Parsing key \"%s\"", furi_string_get_cstr(argument));
|
||||
if(!key->parse(value_str, instance->action_context)) {
|
||||
furi_string_printf(
|
||||
instance->error_message,
|
||||
"Unable to parse value \'%s\' for key \'%s\'",
|
||||
furi_string_get_cstr(value_str),
|
||||
furi_string_get_cstr(argument));
|
||||
result = NfcCliProcessorErrorKeyParseError;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->keys_found[instance->total_keys_found] = key;
|
||||
instance->total_keys_found++;
|
||||
if(key->features.required) instance->required_keys_found++;
|
||||
} while(false);
|
||||
furi_string_free(value_str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static NfcCliProcessorError
|
||||
nfc_cli_parse_group_key(NfcCliProcessorContext* instance, FuriString* argument) {
|
||||
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
||||
FURI_LOG_D(TAG, "Parsing key group\"%s\"", furi_string_get_cstr(argument));
|
||||
|
||||
FuriString* arg_buf = furi_string_alloc();
|
||||
for(size_t i = 0; i < furi_string_size(argument); i++) {
|
||||
furi_string_set_n(arg_buf, argument, i, 1);
|
||||
result = nfc_cli_parse_single_key(instance, arg_buf, NULL, true);
|
||||
if(result != NfcCliProcessorErrorNone) break;
|
||||
}
|
||||
furi_string_free(arg_buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static NfcCliProcessorError nfc_cli_parse_argument(
|
||||
NfcCliProcessorContext* instance,
|
||||
FuriString* argument,
|
||||
FuriString* args) {
|
||||
NfcCliArgumentType type = nfc_cli_get_argument_type(argument);
|
||||
|
||||
furi_string_trim(argument, "-");
|
||||
|
||||
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
||||
|
||||
if(type == NfcCliArgumentTypeShortNameKeyGroup)
|
||||
result = nfc_cli_parse_group_key(instance, argument);
|
||||
else if((type == NfcCliArgumentTypeShortNameKey) || (type == NfcCliArgumentTypeLongNameKey)) {
|
||||
result = nfc_cli_parse_single_key(instance, argument, args, false);
|
||||
} else if(type == NfcCliArgumentTypeUnknown) { //-V547
|
||||
result = NfcCliProcessorErrorKeyNotSupported;
|
||||
furi_string_printf(
|
||||
instance->error_message,
|
||||
"Key \'%s\' is not supported",
|
||||
furi_string_get_cstr(argument));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static NfcCliProcessorError
|
||||
nfc_cli_process_arguments(NfcCliProcessorContext* instance, FuriString* args) {
|
||||
NfcCliProcessorError result = NfcCliProcessorErrorNone;
|
||||
|
||||
FuriString* argument = furi_string_alloc();
|
||||
while(args_read_string_and_trim(args, argument)) {
|
||||
result = nfc_cli_parse_argument(instance, argument, args);
|
||||
if(result != NfcCliProcessorErrorNone) break;
|
||||
}
|
||||
furi_string_free(argument);
|
||||
|
||||
if((result == NfcCliProcessorErrorNone) &&
|
||||
(instance->required_keys_expected != instance->required_keys_found)) {
|
||||
furi_string_printf(instance->error_message, "Some required keys missing");
|
||||
result = NfcCliProcessorErrorKeyRequiredMissing;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void nfc_cli_command_process_error(
|
||||
const NfcCliProcessorContext* instance,
|
||||
NfcCliProcessorError error) {
|
||||
do {
|
||||
if(error == NfcCliProcessorErrorNone) break;
|
||||
|
||||
if(error != NfcCliProcessorErrorNoneButHelp)
|
||||
printf(
|
||||
ANSI_FG_RED "Error: %s\r\n" ANSI_RESET,
|
||||
furi_string_get_cstr(instance->error_message));
|
||||
|
||||
if(error == NfcCliProcessorErrorActionNotFound)
|
||||
nfc_cli_command_format_info(instance->cmd, instance->error_message);
|
||||
else
|
||||
nfc_cli_action_format_info(instance->action, instance->error_message);
|
||||
|
||||
printf("\n%s", furi_string_get_cstr(instance->error_message));
|
||||
} while(false);
|
||||
}
|
||||
|
||||
void nfc_cli_command_processor_run(
|
||||
const NfcCliCommandDescriptor* cmd,
|
||||
PipeSide* pipe,
|
||||
FuriString* args,
|
||||
void* context) {
|
||||
furi_assert(pipe);
|
||||
furi_assert(cmd);
|
||||
furi_assert(args);
|
||||
NfcCliProcessorContext* instance = context;
|
||||
furi_string_reset(instance->error_message);
|
||||
|
||||
NfcCliProcessorError error = NfcCliProcessorErrorNone;
|
||||
instance->cmd = cmd;
|
||||
do {
|
||||
error = nfc_cli_action_alloc(instance, args);
|
||||
if(error != NfcCliProcessorErrorNone) break;
|
||||
|
||||
error = nfc_cli_process_arguments(instance, args);
|
||||
if(error != NfcCliProcessorErrorNone) break;
|
||||
|
||||
if(instance->action && instance->action->execute) {
|
||||
instance->action->execute(pipe, instance->action_context);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Action execute callback missing");
|
||||
}
|
||||
} while(false);
|
||||
|
||||
nfc_cli_command_process_error(instance, error);
|
||||
}
|
||||
|
||||
NfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
NfcCliProcessorContext* instance = malloc(sizeof(NfcCliProcessorContext));
|
||||
instance->nfc = nfc;
|
||||
instance->keys_found = malloc(NFC_CLI_KEYS_FOUND_SIZE_BYTES);
|
||||
instance->total_keys_found = 0;
|
||||
instance->required_keys_found = 0;
|
||||
instance->required_keys_expected = 0;
|
||||
|
||||
instance->error_message = furi_string_alloc();
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_cli_command_processor_free(NfcCliProcessorContext* instance) {
|
||||
furi_assert(instance);
|
||||
nfc_cli_action_free(instance);
|
||||
free(instance->keys_found);
|
||||
furi_string_free(instance->error_message);
|
||||
|
||||
instance->nfc = NULL;
|
||||
free(instance);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <nfc/nfc.h>
|
||||
#include "nfc_cli_command_base.h"
|
||||
|
||||
typedef struct NfcCliProcessorContext NfcCliProcessorContext;
|
||||
|
||||
NfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc);
|
||||
void nfc_cli_command_processor_free(NfcCliProcessorContext* instance);
|
||||
|
||||
void nfc_cli_command_processor_run(
|
||||
const NfcCliCommandDescriptor* cmd,
|
||||
PipeSide* pipe,
|
||||
FuriString* args,
|
||||
void* context);
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "nfc_cli_commands.h"
|
||||
#include "nfc_cli_command_base_i.h"
|
||||
|
||||
/** Include new commands here */
|
||||
#include "commands/raw/nfc_cli_command_raw.h"
|
||||
#include "commands/apdu/nfc_cli_command_apdu.h"
|
||||
#include "commands/dump/nfc_cli_command_dump.h"
|
||||
#include "commands/mfu/nfc_cli_command_mfu.h"
|
||||
#include "commands/nfc_cli_command_emulate.h"
|
||||
#include "commands/nfc_cli_command_scanner.h"
|
||||
#include "commands/nfc_cli_command_field.h"
|
||||
|
||||
#define TAG "NfcCliCommands"
|
||||
|
||||
/** Add new commands here */
|
||||
static const NfcCliCommandDescriptor* nfc_cli_commands[] = {
|
||||
&apdu_cmd,
|
||||
&raw_cmd,
|
||||
&emulate_cmd,
|
||||
&mfu_cmd,
|
||||
&scanner_cmd,
|
||||
&dump_cmd,
|
||||
&field_cmd,
|
||||
};
|
||||
|
||||
size_t nfc_cli_command_get_count() {
|
||||
return COUNT_OF(nfc_cli_commands);
|
||||
}
|
||||
|
||||
const NfcCliActionDescriptor*
|
||||
nfc_cli_command_get_action_by_name(const NfcCliCommandDescriptor* cmd, const FuriString* name) {
|
||||
furi_assert(cmd);
|
||||
furi_assert(name);
|
||||
|
||||
for(size_t i = 0; i < cmd->action_count; i++) {
|
||||
const NfcCliActionDescriptor* action = cmd->actions[i];
|
||||
if(furi_string_equal_str(name, action->name)) return action;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const NfcCliCommandDescriptor* nfc_cli_command_get_by_index(size_t index) {
|
||||
furi_assert(index < COUNT_OF(nfc_cli_commands));
|
||||
return nfc_cli_commands[index];
|
||||
}
|
||||
|
||||
bool nfc_cli_command_has_multiple_actions(const NfcCliCommandDescriptor* cmd) {
|
||||
furi_assert(cmd);
|
||||
furi_check(cmd->action_count > 0);
|
||||
return (cmd->action_count > 1);
|
||||
}
|
||||
|
||||
const char* nfc_cli_command_get_name(const NfcCliCommandDescriptor* cmd) {
|
||||
furi_assert(cmd);
|
||||
return cmd->name;
|
||||
}
|
||||
|
||||
CliCommandExecuteCallback nfc_cli_command_get_execute(const NfcCliCommandDescriptor* cmd) {
|
||||
furi_assert(cmd);
|
||||
return cmd->callback;
|
||||
}
|
||||
|
||||
static inline const NfcCliKeyDescriptor* nfc_cli_action_get_key_by_name(
|
||||
const NfcCliActionDescriptor* action,
|
||||
const FuriString* name,
|
||||
bool long_name) {
|
||||
for(size_t i = 0; i < action->key_count; i++) {
|
||||
const NfcCliKeyDescriptor* key = &action->keys[i];
|
||||
const char* buf = long_name ? key->long_name : key->short_name;
|
||||
if((buf != NULL) && furi_string_equal_str(name, buf)) return key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const NfcCliKeyDescriptor*
|
||||
nfc_cli_action_get_key_descriptor(const NfcCliActionDescriptor* action, FuriString* argument) {
|
||||
furi_assert(action);
|
||||
furi_assert(argument);
|
||||
|
||||
return nfc_cli_action_get_key_by_name(action, argument, furi_string_size(argument) > 1);
|
||||
}
|
||||
|
||||
size_t nfc_cli_action_get_required_keys_count(const NfcCliActionDescriptor* action) {
|
||||
furi_assert(action);
|
||||
|
||||
size_t required_key_count = 0;
|
||||
for(size_t i = 0; i < action->key_count; i++) {
|
||||
const NfcCliKeyDescriptor* key = &action->keys[i];
|
||||
if(!key->features.required) continue;
|
||||
required_key_count++;
|
||||
}
|
||||
return required_key_count;
|
||||
}
|
||||
|
||||
static int nfc_cli_action_format_key_name(const NfcCliKeyDescriptor* key, FuriString* output) {
|
||||
int len = 0;
|
||||
FuriString* name = furi_string_alloc();
|
||||
if(key->short_name && key->long_name) {
|
||||
len = furi_string_printf(name, "-%s, --%s", key->short_name, key->long_name);
|
||||
} else if(key->short_name && (key->long_name == NULL)) {
|
||||
len = furi_string_printf(name, "-%s", key->short_name);
|
||||
} else if((key->short_name == NULL) && key->long_name) {
|
||||
len = furi_string_printf(name, "--%s", key->long_name);
|
||||
}
|
||||
|
||||
const char* color = key->features.required ? ANSI_FLIPPER_BRAND_ORANGE : ANSI_RESET;
|
||||
furi_string_printf(output, "%s%s%s", color, furi_string_get_cstr(name), ANSI_RESET);
|
||||
furi_string_free(name);
|
||||
return len;
|
||||
}
|
||||
|
||||
void nfc_cli_action_format_info(const NfcCliActionDescriptor* action, FuriString* output) {
|
||||
furi_assert(action);
|
||||
furi_assert(output);
|
||||
furi_string_printf(
|
||||
output,
|
||||
action->description ? "%s - %s\r\n\n" : "%s\r\n\n",
|
||||
action->name,
|
||||
action->description);
|
||||
|
||||
if(action->key_count == 0) return;
|
||||
|
||||
FuriString* buf = furi_string_alloc();
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
ANSI_FG_BR_GREEN "Keys " ANSI_FLIPPER_BRAND_ORANGE "(required) " ANSI_RESET
|
||||
"(optional):\r\n");
|
||||
|
||||
for(size_t i = 0; i < action->key_count; i++) {
|
||||
const NfcCliKeyDescriptor* key = &action->keys[i];
|
||||
|
||||
int len = nfc_cli_action_format_key_name(key, buf);
|
||||
furi_string_cat_printf(output, "%s", furi_string_get_cstr(buf));
|
||||
|
||||
if(key->description) {
|
||||
const int offset = 20;
|
||||
furi_string_cat_printf(
|
||||
output, ANSI_CURSOR_RIGHT_BY("%d") "%s", offset - len, key->description);
|
||||
}
|
||||
furi_string_cat_printf(output, "\r\n");
|
||||
}
|
||||
furi_string_free(buf);
|
||||
}
|
||||
|
||||
void nfc_cli_command_format_info(const NfcCliCommandDescriptor* cmd, FuriString* output) {
|
||||
furi_assert(cmd);
|
||||
furi_assert(output);
|
||||
furi_string_printf(output, "%s - %s\r\n", cmd->name, cmd->description);
|
||||
if(cmd->action_count > 1) {
|
||||
furi_string_cat_printf(output, "Possible actions: \r\n");
|
||||
|
||||
for(size_t i = 0; i < cmd->action_count; i++) {
|
||||
const NfcCliActionDescriptor* action = cmd->actions[i];
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
action->description ? "\t%s\t-\t%s\r\n" : "%s\r\n",
|
||||
action->name,
|
||||
action->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "nfc_cli_command_base.h"
|
||||
#include <toolbox/cli/cli_command.h>
|
||||
#include <toolbox/cli/cli_ansi.h>
|
||||
|
||||
size_t nfc_cli_command_get_count();
|
||||
|
||||
const NfcCliCommandDescriptor* nfc_cli_command_get_by_index(size_t index);
|
||||
|
||||
const char* nfc_cli_command_get_name(const NfcCliCommandDescriptor* cmd);
|
||||
|
||||
CliCommandExecuteCallback nfc_cli_command_get_execute(const NfcCliCommandDescriptor* cmd);
|
||||
|
||||
bool nfc_cli_command_has_multiple_actions(const NfcCliCommandDescriptor* cmd);
|
||||
|
||||
const NfcCliActionDescriptor*
|
||||
nfc_cli_command_get_action_by_name(const NfcCliCommandDescriptor* cmd, const FuriString* name);
|
||||
|
||||
size_t nfc_cli_action_get_required_keys_count(const NfcCliActionDescriptor* action);
|
||||
|
||||
const NfcCliKeyDescriptor*
|
||||
nfc_cli_action_get_key_descriptor(const NfcCliActionDescriptor* action, FuriString* argument);
|
||||
|
||||
void nfc_cli_command_format_info(const NfcCliCommandDescriptor* cmd, FuriString* output);
|
||||
|
||||
void nfc_cli_action_format_info(const NfcCliActionDescriptor* action, FuriString* output);
|
||||
@@ -21,7 +21,7 @@ typedef struct {
|
||||
uint32_t ar1;
|
||||
} Mfkey32LoggerParams;
|
||||
|
||||
ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST);
|
||||
ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST); //-V658
|
||||
|
||||
struct Mfkey32Logger {
|
||||
uint32_t cuid;
|
||||
|
||||
@@ -27,7 +27,7 @@ typedef struct {
|
||||
NfcSupportedCardsPluginFeature feature;
|
||||
} NfcSupportedCardsPluginCache;
|
||||
|
||||
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST);
|
||||
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST); //-V658
|
||||
|
||||
typedef enum {
|
||||
NfcSupportedCardsLoadStateIdle,
|
||||
|
||||
@@ -39,17 +39,8 @@ static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent e
|
||||
}
|
||||
|
||||
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
nfc_render_felica_dump(data, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
// Jump to advanced scene right away
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);
|
||||
}
|
||||
|
||||
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {
|
||||
|
||||
@@ -4,9 +4,16 @@ void nfc_render_felica_blocks_count(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
bool render_auth_notification) {
|
||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||
if(data->workflow_type == FelicaLite) {
|
||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||
|
||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||
}
|
||||
} else if(data->workflow_type == FelicaStandard) {
|
||||
furi_string_cat_printf(
|
||||
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +39,11 @@ void nfc_render_felica_info(
|
||||
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
||||
}
|
||||
|
||||
FuriString* ic_type_str = furi_string_alloc();
|
||||
felica_get_ic_name(data, ic_type_str);
|
||||
furi_string_cat_printf(str, "IC Type:\n%s\n", furi_string_get_cstr(ic_type_str));
|
||||
furi_string_free(ic_type_str);
|
||||
|
||||
nfc_render_felica_idm(data, format_type, str);
|
||||
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
@@ -40,6 +52,14 @@ void nfc_render_felica_info(
|
||||
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n");
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"Services found: %lu \nAreas found: %lu\n",
|
||||
simple_array_get_count(data->services),
|
||||
simple_array_get_count(data->areas));
|
||||
|
||||
nfc_render_felica_blocks_count(data, str, true);
|
||||
}
|
||||
|
||||
@@ -59,13 +79,18 @@ static void nfc_render_felica_block_name(
|
||||
|
||||
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
||||
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
|
||||
furi_string_cat_printf(str, "%02X ", block->data[j]);
|
||||
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
|
||||
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
|
||||
}
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {
|
||||
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
|
||||
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block(
|
||||
const FelicaBlock* block,
|
||||
FuriString* str,
|
||||
@@ -76,8 +101,13 @@ static void nfc_render_felica_block(
|
||||
nfc_render_felica_block_data(block, str);
|
||||
}
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {
|
||||
FuriString* name = furi_string_alloc();
|
||||
|
||||
furi_string_cat_printf(str, "\e#Blocks read:\n");
|
||||
|
||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||
|
||||
for(size_t i = 0; i < 14; i++) {
|
||||
furi_string_printf(name, "S_PAD%d", i);
|
||||
uint8_t suf_cnt = 18;
|
||||
@@ -105,3 +135,70 @@ void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||
}
|
||||
|
||||
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
|
||||
const size_t area_count = simple_array_get_count(data->areas);
|
||||
const size_t service_count = simple_array_get_count(data->services);
|
||||
|
||||
furi_string_cat_printf(str, "\e#Directory Tree:\n");
|
||||
|
||||
if(area_count == 0 || service_count == 0) {
|
||||
furi_string_cat_printf(str, "No services or areas found.\n");
|
||||
} else {
|
||||
furi_string_cat_printf(
|
||||
str, "%zu areas found.\n%zu services found.\n\n", area_count, service_count);
|
||||
furi_string_cat_printf(
|
||||
str, "::: ... are readable services\n||| ... are locked services\n");
|
||||
}
|
||||
felica_write_directory_tree(data, str);
|
||||
}
|
||||
|
||||
void nfc_more_info_render_felica_blocks(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
const uint16_t service_code_key) {
|
||||
furi_string_cat_printf(str, "\n");
|
||||
if(data->workflow_type == FelicaLite) {
|
||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||
FuriString* name = furi_string_alloc();
|
||||
|
||||
for(size_t i = 0; i < 14; i++) {
|
||||
furi_string_printf(name, "S_PAD%d", i);
|
||||
uint8_t suf_cnt = 18;
|
||||
if(i == 1) {
|
||||
suf_cnt = 19;
|
||||
} else if((i == 10) || (i == 12) || (i == 13)) {
|
||||
suf_cnt = 16;
|
||||
}
|
||||
nfc_render_felica_block(
|
||||
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
|
||||
}
|
||||
furi_string_free(name);
|
||||
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
|
||||
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
|
||||
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
|
||||
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
|
||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||
|
||||
} else if(data->workflow_type == FelicaStandard) {
|
||||
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
|
||||
for(size_t i = 0; i < public_blocks_count; i++) {
|
||||
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
||||
if(public_block->service_code != service_code_key) {
|
||||
continue; // Skip blocks not matching the requested service code
|
||||
}
|
||||
furi_string_cat_printf(str, "-----Block 0x%02X-----\n", public_block->block_idx);
|
||||
nfc_render_felica_block_data_simple(&public_block->block, str);
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,16 @@ void nfc_render_felica_info(
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
|
||||
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);
|
||||
|
||||
void nfc_render_felica_idm(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);
|
||||
|
||||
void nfc_more_info_render_felica_blocks(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
const uint16_t service_code_key);
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli_main_commands.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/hex.h>
|
||||
#include <lib/toolbox/bit_buffer.h>
|
||||
#include <lib/nfc/nfc_poller.h>
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
|
||||
#include <toolbox/pipe.h>
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
#define FLAG_EVENT (1 << 10)
|
||||
|
||||
#define NFC_MAX_BUFFER_SIZE (256)
|
||||
#define NFC_BASE_PROTOCOL_MAX (2)
|
||||
#define POLLER_DONE (1 << 0)
|
||||
#define POLLER_ERR (1 << 1)
|
||||
static NfcProtocol BASE_PROTOCOL[NFC_BASE_PROTOCOL_MAX] = {
|
||||
NfcProtocolIso14443_4a,
|
||||
NfcProtocolIso14443_4b};
|
||||
typedef struct ApduContext {
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
bool ready;
|
||||
FuriThreadId thread_id;
|
||||
} ApduContext;
|
||||
|
||||
static void nfc_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("nfc <cmd>\r\n");
|
||||
printf("Cmd list:\r\n");
|
||||
printf("\tapdu\t - Send APDU and print response \r\n");
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
printf("\tfield\t - turn field on\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_cli_field(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(args);
|
||||
// Check if nfc worker is not busy
|
||||
if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {
|
||||
printf("NFC chip failed to start\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_nfc_acquire();
|
||||
furi_hal_nfc_low_power_mode_stop();
|
||||
furi_hal_nfc_poller_field_on();
|
||||
|
||||
printf("Field is on. Don't leave device in this mode for too long.\r\n");
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
furi_hal_nfc_low_power_mode_start();
|
||||
furi_hal_nfc_release();
|
||||
}
|
||||
|
||||
static NfcCommand trx_callback(NfcGenericEvent event, void* context) {
|
||||
furi_check(context);
|
||||
ApduContext* apdu_context = (ApduContext*)context;
|
||||
|
||||
if(apdu_context->ready) {
|
||||
apdu_context->ready = false;
|
||||
if(NfcProtocolIso14443_4a == event.protocol) {
|
||||
Iso14443_4aError err = iso14443_4a_poller_send_block(
|
||||
event.instance, apdu_context->tx_buffer, apdu_context->rx_buffer);
|
||||
if(Iso14443_4aErrorNone == err) {
|
||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
|
||||
return NfcCommandContinue;
|
||||
} else {
|
||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
|
||||
return NfcCommandStop;
|
||||
}
|
||||
} else if(NfcProtocolIso14443_4b == event.protocol) {
|
||||
Iso14443_4bError err = iso14443_4b_poller_send_block(
|
||||
event.instance, apdu_context->tx_buffer, apdu_context->rx_buffer);
|
||||
if(Iso14443_4bErrorNone == err) {
|
||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
|
||||
return NfcCommandContinue;
|
||||
} else {
|
||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
|
||||
return NfcCommandStop;
|
||||
}
|
||||
} else {
|
||||
// should never reach here
|
||||
furi_crash("Unknown protocol");
|
||||
}
|
||||
} else {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static void nfc_cli_apdu(PipeSide* pipe, FuriString* args) {
|
||||
UNUSED(pipe);
|
||||
Nfc* nfc = NULL;
|
||||
NfcPoller* poller = NULL;
|
||||
FuriString* data = furi_string_alloc();
|
||||
uint8_t* req_buffer = NULL;
|
||||
uint8_t* resp_buffer = NULL;
|
||||
size_t apdu_size = 0;
|
||||
size_t resp_size = 0;
|
||||
NfcProtocol current_protocol = NfcProtocolInvalid;
|
||||
|
||||
do {
|
||||
if(0 == args_get_first_word_length(args)) {
|
||||
printf(
|
||||
"Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n");
|
||||
break;
|
||||
}
|
||||
nfc = nfc_alloc();
|
||||
|
||||
printf("detecting tag\r\n");
|
||||
for(int i = 0; i < NFC_BASE_PROTOCOL_MAX; i++) {
|
||||
poller = nfc_poller_alloc(nfc, BASE_PROTOCOL[i]);
|
||||
bool is_detected = nfc_poller_detect(poller);
|
||||
nfc_poller_free(poller);
|
||||
if(is_detected) {
|
||||
current_protocol = BASE_PROTOCOL[i];
|
||||
printf("detected tag:%d\r\n", BASE_PROTOCOL[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(NfcProtocolInvalid == current_protocol) {
|
||||
nfc_free(nfc);
|
||||
printf("Can not find any tag\r\n");
|
||||
break;
|
||||
}
|
||||
poller = nfc_poller_alloc(nfc, current_protocol);
|
||||
ApduContext* apdu_context = malloc(sizeof(ApduContext));
|
||||
apdu_context->tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
apdu_context->rx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
apdu_context->ready = false;
|
||||
apdu_context->thread_id = furi_thread_get_current_id();
|
||||
|
||||
nfc_poller_start(poller, trx_callback, apdu_context);
|
||||
while(args_read_string_and_trim(args, data)) {
|
||||
bit_buffer_reset(apdu_context->tx_buffer);
|
||||
bit_buffer_reset(apdu_context->rx_buffer);
|
||||
apdu_size = furi_string_size(data) / 2;
|
||||
req_buffer = malloc(apdu_size);
|
||||
|
||||
hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer);
|
||||
printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data));
|
||||
bit_buffer_copy_bytes(apdu_context->tx_buffer, req_buffer, apdu_size);
|
||||
apdu_context->ready = true;
|
||||
uint32_t flags = furi_thread_flags_wait(POLLER_DONE, FuriFlagWaitAny, 3000);
|
||||
if(0 == (flags & POLLER_DONE)) {
|
||||
printf("Error or Timeout");
|
||||
free(req_buffer);
|
||||
break;
|
||||
}
|
||||
furi_assert(apdu_context->ready == false);
|
||||
resp_size = bit_buffer_get_size_bytes(apdu_context->rx_buffer) * 2;
|
||||
if(!resp_size) {
|
||||
printf("No response\r\n");
|
||||
free(req_buffer);
|
||||
continue;
|
||||
}
|
||||
resp_buffer = malloc(resp_size);
|
||||
uint8_to_hex_chars(
|
||||
bit_buffer_get_data(apdu_context->rx_buffer), resp_buffer, resp_size);
|
||||
resp_buffer[resp_size] = 0;
|
||||
printf("Response: %s\r\n", resp_buffer);
|
||||
|
||||
free(req_buffer);
|
||||
free(resp_buffer);
|
||||
}
|
||||
|
||||
nfc_poller_stop(poller);
|
||||
nfc_poller_free(poller);
|
||||
nfc_free(nfc);
|
||||
|
||||
bit_buffer_free(apdu_context->tx_buffer);
|
||||
bit_buffer_free(apdu_context->rx_buffer);
|
||||
free(apdu_context);
|
||||
} while(false);
|
||||
|
||||
furi_string_free(data);
|
||||
}
|
||||
|
||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
nfc_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "apdu") == 0) {
|
||||
nfc_cli_apdu(pipe, args);
|
||||
break;
|
||||
}
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
if(furi_string_cmp_str(cmd, "field") == 0) {
|
||||
nfc_cli_field(pipe, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nfc_cli_print_usage();
|
||||
} while(false);
|
||||
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
CLI_COMMAND_INTERFACE(nfc, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||
@@ -0,0 +1,678 @@
|
||||
// https://sega.bsnk.me/allnet/amusement_ic/
|
||||
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
#include <flipper_application.h>
|
||||
#include <nfc/protocols/felica/felica.h>
|
||||
#include <bit_lib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "Amusement IC"
|
||||
|
||||
#define N_TABLES 8
|
||||
#define ITERATION_ADD 5
|
||||
|
||||
static const uint8_t s_box[9][256] = {
|
||||
{0x24, 0x3c, 0xba, 0x36, 0xe3, 0x85, 0xa4, 0xd0, 0x93, 0x43, 0x73, 0xb9, 0x70, 0x6e, 0xc9,
|
||||
0xf1, 0x10, 0x0e, 0x9b, 0x2c, 0x97, 0xe7, 0x0b, 0x63, 0x6c, 0x29, 0x20, 0xfe, 0x86, 0xf3,
|
||||
0xe1, 0xf5, 0xf6, 0x9f, 0xb6, 0x16, 0x04, 0x7b, 0x8f, 0xab, 0xb1, 0x39, 0x2a, 0x1b, 0xeb,
|
||||
0x5c, 0xa8, 0xac, 0x38, 0x11, 0x12, 0x5f, 0x89, 0x3e, 0x7d, 0xca, 0xec, 0x53, 0xdb, 0x6d,
|
||||
0x1e, 0xd2, 0x81, 0x78, 0x96, 0x46, 0xff, 0xf9, 0x54, 0x1c, 0x28, 0x7a, 0x4f, 0xd3, 0xc0,
|
||||
0xdc, 0xc1, 0x6a, 0xf2, 0xbc, 0xcb, 0x57, 0xfd, 0x4a, 0xe4, 0xf0, 0xb2, 0xc7, 0x95, 0x40,
|
||||
0x62, 0x52, 0x41, 0xe2, 0xad, 0x49, 0xa6, 0xb5, 0x1f, 0x02, 0xc8, 0xda, 0x92, 0xe5, 0xb0,
|
||||
0xc5, 0x64, 0x76, 0x48, 0x21, 0xde, 0x0f, 0x45, 0x58, 0x4c, 0xdf, 0xa7, 0x84, 0xcf, 0xd5,
|
||||
0x15, 0x4e, 0x27, 0x80, 0x6b, 0x7f, 0xfc, 0x44, 0x71, 0x47, 0x22, 0xdd, 0x30, 0x0c, 0xa1,
|
||||
0x3b, 0xe0, 0x37, 0xa0, 0x35, 0x23, 0x90, 0x32, 0x74, 0xbf, 0x8d, 0xc2, 0xea, 0xd6, 0x50,
|
||||
0xbb, 0xd9, 0xc4, 0x83, 0xb4, 0x31, 0x68, 0x55, 0x8b, 0x5d, 0xf4, 0x72, 0x18, 0xb7, 0xef,
|
||||
0xf7, 0x98, 0x2d, 0x01, 0x03, 0x61, 0xcc, 0x0a, 0x8e, 0x13, 0x00, 0xe8, 0x14, 0xaf, 0x09,
|
||||
0x51, 0x75, 0xa5, 0x2f, 0x1d, 0x0d, 0x65, 0x8a, 0xcd, 0x66, 0x07, 0x3d, 0x05, 0xd8, 0x4b,
|
||||
0xe9, 0x9d, 0x99, 0x7c, 0x91, 0xd1, 0xb8, 0x19, 0xc3, 0x2b, 0x42, 0x69, 0x88, 0xc6, 0x79,
|
||||
0x17, 0x3a, 0x4d, 0x5b, 0xa2, 0x5a, 0xfb, 0x25, 0x3f, 0xbd, 0xf8, 0xed, 0xce, 0xe6, 0x87,
|
||||
0xa3, 0x26, 0xa9, 0x2e, 0xbe, 0x94, 0x08, 0x7e, 0x67, 0x60, 0x8c, 0x9c, 0x5e, 0x6f, 0xb3,
|
||||
0x9e, 0x06, 0xfa, 0x82, 0xae, 0xee, 0x59, 0x77, 0xd7, 0x1a, 0x9a, 0x34, 0xd4, 0x56, 0xaa,
|
||||
0x33},
|
||||
{0xf9, 0x81, 0x7c, 0x00, 0xb9, 0x30, 0x37, 0xd5, 0x90, 0x51, 0x6e, 0xf0, 0xb2, 0x06, 0xfb,
|
||||
0xcd, 0x39, 0x14, 0x5f, 0xf8, 0x1b, 0xc7, 0x4a, 0x82, 0x70, 0x8c, 0x92, 0x1d, 0xea, 0xc3,
|
||||
0x4e, 0xc8, 0x12, 0xe6, 0xb6, 0x10, 0xd3, 0x2f, 0x95, 0x84, 0x25, 0x42, 0xa5, 0x72, 0xe5,
|
||||
0x8f, 0x55, 0xef, 0x86, 0xa2, 0x53, 0xae, 0xed, 0x26, 0x20, 0x47, 0xde, 0x78, 0x68, 0x28,
|
||||
0xe4, 0x45, 0xcc, 0x35, 0xbc, 0x3e, 0xbb, 0x8e, 0xc0, 0xbe, 0x34, 0xaf, 0xa6, 0x09, 0x64,
|
||||
0x01, 0x7b, 0x44, 0xf5, 0xf3, 0xb1, 0x3f, 0xa4, 0xb3, 0x32, 0x80, 0xc9, 0x4f, 0x6f, 0x40,
|
||||
0xbf, 0x21, 0x4c, 0x74, 0xe1, 0x11, 0x5e, 0xeb, 0x3d, 0x71, 0x2e, 0x9d, 0x62, 0x75, 0xd4,
|
||||
0x16, 0x48, 0x77, 0x13, 0x67, 0xad, 0x6b, 0x5d, 0x07, 0x87, 0xf7, 0xa8, 0x9a, 0x59, 0x76,
|
||||
0x8b, 0x9b, 0x1e, 0xd8, 0xee, 0xaa, 0xe9, 0x99, 0x2d, 0xc5, 0x97, 0xf1, 0xa7, 0x83, 0xfc,
|
||||
0xca, 0xdc, 0xba, 0xb8, 0x4b, 0xe8, 0x89, 0x17, 0x05, 0x0b, 0xa0, 0x65, 0x23, 0xd1, 0xce,
|
||||
0x36, 0x03, 0xd7, 0xe0, 0xf2, 0x91, 0xdf, 0x0e, 0x9c, 0x2c, 0x0d, 0x66, 0xd6, 0x73, 0x58,
|
||||
0x5c, 0xb4, 0x0a, 0x4d, 0xc2, 0x3b, 0xd2, 0xb7, 0xe2, 0xac, 0x33, 0xdb, 0x60, 0x27, 0xbd,
|
||||
0x56, 0x43, 0x24, 0x04, 0x02, 0x8d, 0x7d, 0x7f, 0xf6, 0xb5, 0xf4, 0xfd, 0xdd, 0x5b, 0xec,
|
||||
0x79, 0xc4, 0x22, 0x1f, 0xa1, 0x88, 0x54, 0xe7, 0x19, 0x98, 0x94, 0x7e, 0x31, 0xa3, 0x29,
|
||||
0xe3, 0x5a, 0xcb, 0x6a, 0xb0, 0xcf, 0x6d, 0x93, 0x6c, 0x7a, 0x08, 0xa9, 0x3c, 0x1c, 0xd0,
|
||||
0x63, 0x50, 0xc6, 0x85, 0x38, 0xc1, 0x41, 0x49, 0xab, 0x61, 0x52, 0x2a, 0x9f, 0xd9, 0x18,
|
||||
0x1a, 0x57, 0x46, 0x2b, 0x69, 0xda, 0xfa, 0xff, 0x0f, 0x15, 0x96, 0x3a, 0x8a, 0xfe, 0x9e,
|
||||
0x0c},
|
||||
{0x11, 0xa2, 0x9a, 0xc3, 0x94, 0xa0, 0x51, 0x01, 0x5b, 0xf7, 0xd1, 0x30, 0xee, 0x1f, 0x06,
|
||||
0xd5, 0x77, 0x67, 0xd3, 0xc1, 0x6e, 0xe7, 0x6a, 0x1a, 0xa4, 0x8e, 0x9f, 0x65, 0x66, 0xd6,
|
||||
0x0c, 0x2d, 0xc6, 0xe4, 0x69, 0xb7, 0x56, 0x5a, 0x57, 0x60, 0xfc, 0x79, 0x1e, 0xe1, 0x16,
|
||||
0x52, 0xf0, 0x07, 0xd0, 0xcd, 0xca, 0x78, 0xc9, 0x8b, 0xb3, 0x88, 0x27, 0xf6, 0xe0, 0xc0,
|
||||
0x84, 0xcf, 0x2f, 0xa3, 0x04, 0x00, 0x80, 0x40, 0xa7, 0xfa, 0x75, 0x9d, 0x4b, 0x89, 0x46,
|
||||
0x22, 0x28, 0x37, 0x34, 0x13, 0xff, 0x20, 0xb4, 0xda, 0x08, 0x26, 0x6c, 0x8f, 0xf2, 0x5d,
|
||||
0x7b, 0x73, 0x5c, 0x4c, 0x90, 0x71, 0x41, 0x8a, 0x9e, 0xbb, 0x12, 0xe8, 0x55, 0x6d, 0x2a,
|
||||
0x25, 0x4a, 0xaf, 0xbd, 0x95, 0x19, 0xa5, 0x31, 0xba, 0xad, 0xd9, 0xc5, 0xa6, 0x6b, 0x83,
|
||||
0xc4, 0xb6, 0xa9, 0xcc, 0xf9, 0xa1, 0xb1, 0x7a, 0xdc, 0xc7, 0xdb, 0x2e, 0x7e, 0x7f, 0x8d,
|
||||
0x4e, 0x62, 0x0f, 0x03, 0x39, 0xfd, 0xb8, 0xd4, 0x18, 0xc2, 0xec, 0x61, 0x02, 0x53, 0x1c,
|
||||
0x3b, 0x7d, 0xbc, 0x1d, 0x59, 0xf5, 0x8c, 0x98, 0xf1, 0x6f, 0x93, 0x85, 0xf8, 0x2c, 0x74,
|
||||
0xd8, 0x5e, 0x4d, 0x97, 0xab, 0x87, 0x82, 0x0a, 0x21, 0x42, 0x5f, 0x0d, 0x58, 0x33, 0x44,
|
||||
0x3a, 0xdd, 0xe9, 0xfb, 0x50, 0x47, 0xc8, 0xd7, 0x81, 0x9b, 0xe5, 0x1b, 0x17, 0x54, 0x86,
|
||||
0x2b, 0xef, 0xf4, 0x29, 0x32, 0x0e, 0x7c, 0x23, 0x15, 0xeb, 0xe6, 0x3c, 0x35, 0xe2, 0x96,
|
||||
0x68, 0x3f, 0x0b, 0x43, 0xce, 0xde, 0x9c, 0xbe, 0x4f, 0x45, 0x72, 0x76, 0xb5, 0x10, 0xe3,
|
||||
0xdf, 0x92, 0xd2, 0xa8, 0x48, 0x91, 0xb9, 0xac, 0xbf, 0x49, 0xb2, 0x99, 0x64, 0x70, 0xea,
|
||||
0xf3, 0x3e, 0x63, 0x14, 0xae, 0xed, 0xaa, 0x24, 0xfe, 0x3d, 0xcb, 0xb0, 0x09, 0x38, 0x36,
|
||||
0x05},
|
||||
{0xc4, 0xf0, 0x28, 0x49, 0x55, 0x4a, 0xf5, 0xfd, 0x75, 0xaf, 0x20, 0x69, 0xc8, 0x43, 0x86,
|
||||
0x6b, 0xc9, 0xa8, 0xc6, 0x54, 0x4c, 0xdd, 0x02, 0x5b, 0xe8, 0x9b, 0x59, 0x77, 0x34, 0xd7,
|
||||
0xc0, 0x51, 0x5f, 0xf3, 0x7e, 0xd4, 0xf1, 0x90, 0x81, 0xce, 0x19, 0x8a, 0x78, 0x33, 0xcd,
|
||||
0x97, 0x8c, 0xd6, 0x6a, 0x4b, 0x8f, 0xa4, 0x80, 0xdc, 0x1a, 0x17, 0x14, 0xc5, 0x07, 0x87,
|
||||
0x66, 0xad, 0x9d, 0x85, 0xb2, 0xf8, 0x8d, 0x98, 0xdf, 0x3e, 0x1f, 0x3f, 0x64, 0x58, 0x40,
|
||||
0xac, 0x6c, 0x5c, 0x08, 0x5d, 0x53, 0xba, 0xff, 0xd2, 0xa9, 0x2c, 0x25, 0xab, 0xe4, 0x32,
|
||||
0x38, 0x9a, 0xf9, 0xcb, 0xc2, 0x91, 0x67, 0xd0, 0xe3, 0x22, 0x29, 0x2d, 0x92, 0x48, 0xb4,
|
||||
0x0e, 0x99, 0x05, 0xa3, 0x6f, 0x0a, 0x15, 0xef, 0xd5, 0x10, 0x4f, 0xd8, 0xf6, 0x00, 0xe6,
|
||||
0x46, 0x2b, 0x31, 0x57, 0x83, 0x94, 0xae, 0x03, 0xeb, 0x8e, 0x13, 0x24, 0xa1, 0x1e, 0x5e,
|
||||
0xd1, 0x26, 0xd9, 0xa7, 0xca, 0x36, 0x6e, 0x71, 0x37, 0xee, 0xb1, 0xfa, 0x7c, 0x76, 0x06,
|
||||
0xb8, 0x23, 0xbe, 0x21, 0xbc, 0x9e, 0xc1, 0xda, 0x7a, 0x3b, 0x62, 0x27, 0x7b, 0xb0, 0xe5,
|
||||
0x63, 0x18, 0x82, 0x7f, 0x0f, 0xed, 0xa0, 0x2a, 0x9c, 0xbf, 0x11, 0xbd, 0xe0, 0x88, 0x0d,
|
||||
0x3a, 0x79, 0x52, 0x56, 0xf7, 0xb6, 0xd3, 0x09, 0x16, 0x1b, 0x70, 0xb3, 0x42, 0x60, 0x2f,
|
||||
0x1c, 0xa2, 0x9f, 0x72, 0x12, 0x45, 0x47, 0xbb, 0xe1, 0x0b, 0x01, 0x8b, 0x1d, 0xde, 0xec,
|
||||
0x0c, 0x4e, 0xe9, 0xcf, 0xcc, 0x95, 0x74, 0xf4, 0xa5, 0x93, 0xc7, 0xdb, 0x4d, 0xfb, 0x35,
|
||||
0x65, 0xfe, 0xe7, 0x39, 0xb5, 0xea, 0x96, 0xc3, 0x04, 0x41, 0x44, 0x84, 0xfc, 0x6d, 0x30,
|
||||
0xe2, 0xf2, 0x68, 0xb7, 0x89, 0x7d, 0x73, 0x3d, 0xb9, 0x5a, 0x50, 0xa6, 0x3c, 0x61, 0xaa,
|
||||
0x2e},
|
||||
{0x1a, 0x59, 0x9c, 0xad, 0xc8, 0xe4, 0x11, 0x54, 0xed, 0x37, 0x0f, 0x3a, 0xe6, 0x5f, 0x3c,
|
||||
0x4b, 0xb8, 0x15, 0x89, 0xb1, 0xe8, 0xda, 0x69, 0x77, 0x91, 0x56, 0x8b, 0xdb, 0x06, 0x24,
|
||||
0xcf, 0x18, 0xf8, 0xb0, 0x87, 0xdf, 0x8c, 0x35, 0xcb, 0x86, 0x53, 0x9d, 0xa4, 0x66, 0x4a,
|
||||
0x7a, 0x71, 0x0a, 0x48, 0x38, 0xff, 0xdc, 0x83, 0x20, 0xce, 0x98, 0x32, 0xf6, 0xd7, 0xaf,
|
||||
0x70, 0x5e, 0x73, 0x8a, 0x14, 0x72, 0x1e, 0xc1, 0x29, 0x79, 0x07, 0x08, 0xe9, 0x43, 0x46,
|
||||
0xd0, 0x2f, 0xde, 0x2a, 0x4f, 0x3d, 0x2c, 0x50, 0xb3, 0x75, 0xfc, 0x0b, 0x64, 0xae, 0x31,
|
||||
0x7b, 0x61, 0xa5, 0x30, 0xa0, 0x93, 0xd2, 0xbe, 0xc2, 0x55, 0xba, 0x6c, 0xf3, 0x62, 0x68,
|
||||
0xfd, 0xac, 0x3b, 0x95, 0x49, 0x1f, 0x6b, 0xb4, 0x85, 0xf7, 0xa6, 0x03, 0xec, 0x6e, 0x9a,
|
||||
0x81, 0x09, 0x0c, 0x6a, 0xee, 0x9e, 0x4e, 0xf0, 0xab, 0x2d, 0x7e, 0xa1, 0xe1, 0xbb, 0xc9,
|
||||
0xbc, 0x41, 0xf2, 0xfa, 0x2e, 0x1b, 0xdd, 0x27, 0x34, 0xd3, 0x60, 0x04, 0xb5, 0x01, 0xd6,
|
||||
0x40, 0xf9, 0xd5, 0x02, 0x47, 0xd9, 0xd8, 0xe0, 0x8f, 0x2b, 0xfb, 0xb7, 0xc5, 0xeb, 0x57,
|
||||
0x16, 0xe5, 0x78, 0x8d, 0xf4, 0x00, 0xcd, 0x82, 0x39, 0xc6, 0x96, 0xbd, 0xbf, 0xe3, 0x36,
|
||||
0x45, 0x0e, 0x26, 0x1d, 0x63, 0xe7, 0x84, 0x3e, 0xb6, 0x9b, 0x22, 0xa3, 0xf5, 0x8e, 0x23,
|
||||
0x6d, 0xc3, 0x99, 0x7d, 0x90, 0x97, 0x10, 0x25, 0x80, 0xd4, 0x4c, 0xe2, 0x74, 0xcc, 0xb2,
|
||||
0x5c, 0x33, 0xc7, 0xea, 0x05, 0x12, 0x3f, 0x51, 0xa8, 0xfe, 0xf1, 0x1c, 0x42, 0xca, 0xd1,
|
||||
0x76, 0x28, 0x6f, 0x92, 0xa7, 0x67, 0xa9, 0x19, 0xa2, 0x44, 0x5b, 0xaa, 0xef, 0x58, 0x7f,
|
||||
0x21, 0xc0, 0x88, 0x65, 0xb9, 0x5d, 0x4d, 0x0d, 0x5a, 0xc4, 0x13, 0x52, 0x7c, 0x9f, 0x94,
|
||||
0x17},
|
||||
{0xc7, 0x2b, 0x82, 0x61, 0x5b, 0xd0, 0x96, 0x84, 0xd3, 0x4a, 0x70, 0xa1, 0x9b, 0x59, 0x33,
|
||||
0x9f, 0xc0, 0x20, 0x14, 0x53, 0x29, 0x17, 0xc5, 0x0b, 0xc9, 0x7b, 0x97, 0x02, 0x0d, 0x3a,
|
||||
0x1e, 0x7c, 0x3f, 0x6b, 0x52, 0xe8, 0x75, 0x3d, 0xf6, 0xe4, 0x0c, 0x8a, 0x4f, 0xc3, 0x5f,
|
||||
0x26, 0x65, 0x73, 0x31, 0x23, 0x28, 0x48, 0x74, 0xaa, 0xa7, 0x36, 0x09, 0xb1, 0xe2, 0x91,
|
||||
0x04, 0x51, 0x22, 0xfc, 0x08, 0xa6, 0x05, 0xa4, 0xf1, 0x12, 0x1c, 0x19, 0xeb, 0x40, 0x37,
|
||||
0xc2, 0xa0, 0x41, 0x1d, 0xd4, 0xdc, 0x07, 0x43, 0x8f, 0x47, 0xaf, 0xd1, 0x2e, 0x98, 0xab,
|
||||
0x01, 0xba, 0xf0, 0x66, 0x68, 0xac, 0xf9, 0xe7, 0x69, 0xb6, 0xcb, 0x8d, 0x78, 0x87, 0x15,
|
||||
0x03, 0xd5, 0xdf, 0xa3, 0x1a, 0x9d, 0x6a, 0xea, 0x2f, 0x94, 0x4e, 0x9e, 0x42, 0xd7, 0xb8,
|
||||
0x38, 0x92, 0xd8, 0xbb, 0xde, 0xdd, 0x9a, 0xbc, 0xb0, 0x4c, 0x79, 0xf4, 0x58, 0x3e, 0xe9,
|
||||
0x83, 0x81, 0xff, 0xe3, 0x55, 0xfd, 0x5d, 0xb2, 0xef, 0x9c, 0x6d, 0x54, 0x99, 0x60, 0xda,
|
||||
0x3b, 0xec, 0xfa, 0x11, 0xd6, 0xc4, 0x2a, 0xed, 0x4b, 0xae, 0x13, 0xbf, 0xb9, 0x06, 0x8b,
|
||||
0xe0, 0x1f, 0x7f, 0x5a, 0xad, 0x90, 0x39, 0x0f, 0xf3, 0xbd, 0x46, 0x6c, 0x2c, 0xf2, 0xf8,
|
||||
0xfe, 0xd9, 0xe6, 0x72, 0x0e, 0x89, 0xbe, 0x5e, 0xc6, 0xa8, 0xcf, 0xf5, 0x57, 0x7e, 0x8c,
|
||||
0xb7, 0xe1, 0x88, 0x7a, 0xc8, 0x1b, 0x18, 0xdb, 0x6f, 0x35, 0xd2, 0x16, 0x32, 0x64, 0x63,
|
||||
0xa5, 0x8e, 0xf7, 0xb4, 0x76, 0x95, 0x86, 0x7d, 0xe5, 0xa9, 0x5c, 0x85, 0x00, 0xc1, 0x93,
|
||||
0x4d, 0xfb, 0x30, 0xa2, 0xb5, 0x25, 0x34, 0x10, 0x77, 0x3c, 0x67, 0x71, 0x2d, 0x44, 0x45,
|
||||
0x56, 0x0a, 0x50, 0xb3, 0xcd, 0x62, 0x80, 0xce, 0x21, 0x49, 0x24, 0xca, 0xee, 0x27, 0x6e,
|
||||
0xcc},
|
||||
{0xa5, 0xb0, 0x6d, 0xb2, 0x51, 0x55, 0x1f, 0xbf, 0x3e, 0x8d, 0xdd, 0x19, 0x92, 0xea, 0x1b,
|
||||
0xbd, 0x32, 0x65, 0x29, 0xa4, 0x89, 0x67, 0xb1, 0x90, 0x68, 0x00, 0xda, 0x0d, 0xa6, 0x59,
|
||||
0x54, 0xf2, 0x10, 0x14, 0x2f, 0x45, 0xe0, 0x3b, 0x23, 0xfa, 0xd7, 0x50, 0xac, 0x93, 0xe6,
|
||||
0x43, 0x01, 0x0a, 0x5c, 0x78, 0x70, 0x98, 0x46, 0xa9, 0x7b, 0xf3, 0x95, 0x07, 0x2a, 0xd3,
|
||||
0x74, 0xb3, 0x3f, 0xa3, 0x60, 0x82, 0x39, 0x4e, 0x34, 0x48, 0xc0, 0x1c, 0xf6, 0xc2, 0x91,
|
||||
0x64, 0x4d, 0x3c, 0xf8, 0x9d, 0x35, 0x9a, 0x94, 0xe3, 0x7a, 0xf0, 0xf9, 0x6e, 0xb9, 0x12,
|
||||
0xb8, 0x04, 0x2d, 0x02, 0x28, 0x13, 0x85, 0x72, 0x80, 0x87, 0xdb, 0x2b, 0xf1, 0x4f, 0x26,
|
||||
0xa2, 0xe1, 0x49, 0x7e, 0x9c, 0xcc, 0xa7, 0xb6, 0xa0, 0xd0, 0x9b, 0x36, 0x77, 0xad, 0x8b,
|
||||
0x6b, 0x4a, 0x03, 0x1d, 0x05, 0x8a, 0x06, 0x4b, 0xaf, 0xe5, 0x31, 0xb5, 0xd4, 0xc6, 0x0c,
|
||||
0x66, 0xba, 0x83, 0xfd, 0x09, 0x0f, 0xae, 0x71, 0xb7, 0x6f, 0xdc, 0x41, 0xe8, 0x17, 0x8e,
|
||||
0x40, 0x7f, 0x62, 0x30, 0xff, 0xa8, 0x84, 0x25, 0xfb, 0x16, 0xce, 0x37, 0x44, 0xab, 0x99,
|
||||
0x1e, 0xeb, 0x18, 0x3a, 0x47, 0xf7, 0x5f, 0x81, 0xcf, 0xed, 0x58, 0x2c, 0x6c, 0xfe, 0x9e,
|
||||
0x57, 0x53, 0x97, 0x20, 0xca, 0x79, 0x1a, 0x5a, 0x88, 0xf5, 0x69, 0x9f, 0xe7, 0xd9, 0x0e,
|
||||
0xbe, 0x42, 0xdf, 0x56, 0xe4, 0x4c, 0x22, 0xaa, 0x73, 0x0b, 0x15, 0xc5, 0xee, 0xfc, 0xc7,
|
||||
0xd6, 0xcb, 0xcd, 0x8c, 0xe2, 0x76, 0x21, 0xe9, 0xd1, 0xec, 0xc8, 0x7d, 0xd8, 0x8f, 0x61,
|
||||
0x7c, 0x2e, 0xbc, 0xde, 0xb4, 0x75, 0xd2, 0xc4, 0x63, 0x3d, 0xa1, 0x5e, 0x5d, 0x6a, 0x08,
|
||||
0x24, 0xc9, 0x27, 0xbb, 0xef, 0x33, 0x86, 0x5b, 0xd5, 0x38, 0x52, 0x11, 0xf4, 0xc3, 0x96,
|
||||
0xc1},
|
||||
{0x34, 0x5a, 0xdb, 0x2c, 0x59, 0x57, 0x12, 0x2b, 0x30, 0xc2, 0xa0, 0x92, 0xbf, 0xed, 0xbc,
|
||||
0x45, 0xde, 0x27, 0x9b, 0x96, 0xd3, 0xe6, 0xc5, 0xeb, 0xd8, 0x24, 0x4b, 0xa4, 0x21, 0xcc,
|
||||
0xa8, 0xd6, 0xce, 0x3c, 0xba, 0xb1, 0x09, 0xe0, 0xd7, 0x32, 0x66, 0x4a, 0x83, 0x1d, 0x19,
|
||||
0xca, 0x89, 0x67, 0x0f, 0x42, 0x07, 0x71, 0xe9, 0xbb, 0x44, 0x5e, 0x85, 0x17, 0xc4, 0xae,
|
||||
0x9c, 0x3f, 0x6b, 0x78, 0xe7, 0x6d, 0x02, 0xa7, 0xfe, 0x33, 0xe3, 0xd9, 0x0a, 0x7d, 0xea,
|
||||
0xf1, 0x18, 0x87, 0x7b, 0x62, 0x03, 0x79, 0x52, 0x23, 0x00, 0x3e, 0x25, 0xb9, 0xdd, 0x16,
|
||||
0x68, 0x3b, 0x1f, 0x90, 0xa6, 0x2a, 0xc6, 0x29, 0x91, 0x8a, 0x9d, 0xef, 0x1e, 0x84, 0x76,
|
||||
0xee, 0x4f, 0x39, 0xfb, 0x11, 0x1b, 0x0b, 0x93, 0xad, 0x49, 0xb7, 0x05, 0xe1, 0x4d, 0xcb,
|
||||
0xe8, 0x38, 0xd0, 0x55, 0xf2, 0xf7, 0x0d, 0x8b, 0x65, 0xcd, 0xfa, 0xd4, 0x9e, 0xc9, 0x81,
|
||||
0x4e, 0x14, 0xc8, 0x26, 0xb6, 0xf9, 0xaa, 0xf5, 0x80, 0xf8, 0x6a, 0x98, 0xab, 0x58, 0xf3,
|
||||
0xb8, 0x8e, 0xb5, 0x97, 0x43, 0x72, 0xa2, 0xda, 0x64, 0xc1, 0x40, 0x5c, 0x13, 0xb0, 0xe5,
|
||||
0xbd, 0x08, 0x9f, 0x1c, 0x7e, 0x8f, 0x06, 0xbe, 0x6e, 0x50, 0x1a, 0x2f, 0x37, 0xa1, 0xfc,
|
||||
0x10, 0x2e, 0x70, 0x53, 0x04, 0x20, 0x63, 0x48, 0xe2, 0xfd, 0x6f, 0x7a, 0x75, 0x61, 0xb3,
|
||||
0x35, 0x3a, 0xcf, 0x5b, 0x88, 0x82, 0x51, 0xd2, 0x47, 0xa5, 0xa9, 0x74, 0x6c, 0x31, 0x94,
|
||||
0x7c, 0x9a, 0xc0, 0xec, 0x15, 0x0e, 0x28, 0x01, 0x2d, 0xc3, 0xf0, 0xb4, 0xe4, 0x8d, 0xdc,
|
||||
0xac, 0x3d, 0x5f, 0x86, 0xc7, 0x95, 0x22, 0xf4, 0xb2, 0xa3, 0x54, 0x46, 0xd1, 0x77, 0x8c,
|
||||
0x0c, 0xff, 0xaf, 0x56, 0x5d, 0x36, 0x69, 0x7f, 0x99, 0x4c, 0xd5, 0x60, 0x73, 0xdf, 0x41,
|
||||
0xf6},
|
||||
{0x04, 0xda, 0x79, 0x63, 0x1e, 0xbd, 0xea, 0xe3, 0x0b, 0x65, 0x25, 0x01, 0xcd, 0xa9, 0x92,
|
||||
0xed, 0x18, 0x75, 0x57, 0x30, 0x89, 0x03, 0x09, 0x6a, 0xdc, 0xc7, 0x98, 0xa4, 0x50, 0x91,
|
||||
0x1f, 0xb1, 0x0c, 0x77, 0x85, 0x66, 0xca, 0x12, 0x1c, 0x67, 0xc2, 0xe0, 0x17, 0x83, 0x59,
|
||||
0x9d, 0xd5, 0x22, 0x82, 0x56, 0xa8, 0x9f, 0xc6, 0x60, 0x48, 0xb3, 0x11, 0xfc, 0xa3, 0x28,
|
||||
0xfb, 0x06, 0xdd, 0x5d, 0xba, 0x29, 0x7a, 0x4f, 0xe8, 0xfe, 0xe9, 0x10, 0xcb, 0xf3, 0x93,
|
||||
0x7b, 0x6c, 0x69, 0x54, 0xe7, 0x44, 0xa2, 0x84, 0x1d, 0x8d, 0xce, 0xff, 0xfa, 0x1a, 0x87,
|
||||
0x90, 0x74, 0xa1, 0xf8, 0x14, 0xaa, 0xbc, 0xc0, 0xcf, 0x31, 0xb4, 0xd9, 0xbf, 0xd8, 0x5e,
|
||||
0x26, 0x2d, 0xd0, 0xe4, 0x3f, 0x19, 0xd6, 0x8c, 0x2f, 0xab, 0x39, 0x58, 0x72, 0x6e, 0xdf,
|
||||
0x3b, 0xe6, 0x3c, 0xb6, 0x62, 0x88, 0xde, 0x40, 0xb2, 0x8f, 0x9b, 0x7c, 0x95, 0x43, 0xd1,
|
||||
0x9e, 0xac, 0x9c, 0xd4, 0x23, 0xe1, 0x0e, 0x4b, 0x53, 0x33, 0x46, 0x20, 0x13, 0x34, 0x1b,
|
||||
0x97, 0xf7, 0xf1, 0xc3, 0x61, 0x4a, 0x6f, 0x5a, 0x21, 0x7f, 0x70, 0x2e, 0x55, 0x41, 0x05,
|
||||
0xc5, 0xd7, 0x76, 0xe5, 0x27, 0x15, 0xec, 0x42, 0x5c, 0x4d, 0x78, 0x35, 0x8b, 0xef, 0xd2,
|
||||
0xee, 0xb5, 0xbe, 0xae, 0x02, 0x3a, 0xd3, 0x5f, 0xc4, 0x24, 0xf0, 0xf9, 0x51, 0x4e, 0xeb,
|
||||
0x00, 0x0f, 0xfd, 0xaf, 0x3e, 0xc1, 0x9a, 0x52, 0x86, 0x81, 0x80, 0x7e, 0xf6, 0x2b, 0xcc,
|
||||
0xb9, 0x7d, 0x68, 0xf2, 0xad, 0x99, 0xa7, 0x07, 0x2c, 0x73, 0x38, 0xb0, 0x6b, 0xb7, 0x8e,
|
||||
0x71, 0xa0, 0xf4, 0x3d, 0xa6, 0x0d, 0x37, 0xdb, 0x0a, 0x47, 0x36, 0x16, 0x96, 0x6d, 0x32,
|
||||
0x2a, 0x5b, 0xe2, 0x45, 0x94, 0xf5, 0xa5, 0x4c, 0xc8, 0x8a, 0x49, 0x64, 0xbb, 0x08, 0xc9,
|
||||
0xb8},
|
||||
};
|
||||
|
||||
static const uint8_t access_code_table_1[5][10][100] = {
|
||||
{
|
||||
{11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94,
|
||||
45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14,
|
||||
82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83,
|
||||
10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30,
|
||||
16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8},
|
||||
{28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13,
|
||||
49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84,
|
||||
20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80,
|
||||
98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3,
|
||||
33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83},
|
||||
{4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93,
|
||||
81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29,
|
||||
8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38,
|
||||
36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32,
|
||||
18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12},
|
||||
{80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85,
|
||||
55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24,
|
||||
68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33,
|
||||
18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3,
|
||||
93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84},
|
||||
{48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74,
|
||||
51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30,
|
||||
94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24,
|
||||
71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66,
|
||||
11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32},
|
||||
{23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92,
|
||||
6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65,
|
||||
79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72,
|
||||
57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94,
|
||||
51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73},
|
||||
{89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37,
|
||||
11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42,
|
||||
35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81,
|
||||
90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51,
|
||||
86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56},
|
||||
{40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80,
|
||||
81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45,
|
||||
93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65,
|
||||
57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91,
|
||||
4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89},
|
||||
{96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49,
|
||||
95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55,
|
||||
90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4,
|
||||
31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75,
|
||||
85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74},
|
||||
{75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55,
|
||||
98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56,
|
||||
88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4,
|
||||
54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2,
|
||||
11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63},
|
||||
},
|
||||
{
|
||||
{98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36,
|
||||
40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7,
|
||||
62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38,
|
||||
53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8,
|
||||
91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20},
|
||||
{26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15,
|
||||
28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96,
|
||||
8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54,
|
||||
45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92,
|
||||
84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18},
|
||||
{22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20,
|
||||
86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27,
|
||||
17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13,
|
||||
58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62,
|
||||
73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38},
|
||||
{0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42,
|
||||
82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88,
|
||||
14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36,
|
||||
59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98,
|
||||
17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33},
|
||||
{64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58,
|
||||
54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87,
|
||||
31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2,
|
||||
18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20,
|
||||
14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55},
|
||||
{24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94,
|
||||
0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54,
|
||||
12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66,
|
||||
45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10,
|
||||
11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50},
|
||||
{90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42,
|
||||
56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34,
|
||||
64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60,
|
||||
86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2,
|
||||
44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76},
|
||||
{95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30,
|
||||
62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71,
|
||||
34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37,
|
||||
47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65,
|
||||
12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69},
|
||||
{38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86,
|
||||
81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63,
|
||||
39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15,
|
||||
87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31,
|
||||
84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96},
|
||||
{26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75,
|
||||
40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84,
|
||||
81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95,
|
||||
72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35,
|
||||
60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83},
|
||||
},
|
||||
{
|
||||
{10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46,
|
||||
19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94,
|
||||
0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16,
|
||||
75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26,
|
||||
33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45},
|
||||
{73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89,
|
||||
96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65,
|
||||
63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70,
|
||||
92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67,
|
||||
66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36},
|
||||
{60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82,
|
||||
96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10,
|
||||
43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9,
|
||||
23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61,
|
||||
81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26},
|
||||
{49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18,
|
||||
67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53,
|
||||
10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21,
|
||||
85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99,
|
||||
98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70},
|
||||
{30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99,
|
||||
38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77,
|
||||
49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95,
|
||||
0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74,
|
||||
67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29},
|
||||
{1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58,
|
||||
8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19,
|
||||
2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60,
|
||||
66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43,
|
||||
0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29},
|
||||
{30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66,
|
||||
36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32,
|
||||
76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74,
|
||||
85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8,
|
||||
54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20},
|
||||
{60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62,
|
||||
14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33,
|
||||
47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12,
|
||||
26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20,
|
||||
16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72},
|
||||
{35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71,
|
||||
37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87,
|
||||
99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24,
|
||||
2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75,
|
||||
41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72},
|
||||
{6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49,
|
||||
2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14,
|
||||
56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63,
|
||||
76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33,
|
||||
51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66},
|
||||
},
|
||||
{
|
||||
{93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64,
|
||||
24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14,
|
||||
34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73,
|
||||
20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76,
|
||||
38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1},
|
||||
{78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39,
|
||||
98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3,
|
||||
37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9,
|
||||
94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93,
|
||||
66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53},
|
||||
{78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39,
|
||||
98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3,
|
||||
37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9,
|
||||
94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93,
|
||||
66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53},
|
||||
{41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36,
|
||||
26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61,
|
||||
89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79,
|
||||
68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30,
|
||||
17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96},
|
||||
{74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86,
|
||||
97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2,
|
||||
58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31,
|
||||
83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94,
|
||||
6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18},
|
||||
{35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71,
|
||||
54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51,
|
||||
99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6,
|
||||
61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28,
|
||||
56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85},
|
||||
{8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76,
|
||||
57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16,
|
||||
69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40,
|
||||
11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38,
|
||||
75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81},
|
||||
{34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80,
|
||||
92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67,
|
||||
72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28,
|
||||
55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23,
|
||||
59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0},
|
||||
{7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99,
|
||||
60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88,
|
||||
69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82,
|
||||
29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34,
|
||||
58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92},
|
||||
{96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50,
|
||||
48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20,
|
||||
56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4,
|
||||
47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49,
|
||||
92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75},
|
||||
},
|
||||
{
|
||||
{61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20,
|
||||
58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26,
|
||||
84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72,
|
||||
54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81,
|
||||
22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1},
|
||||
{35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2,
|
||||
44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93,
|
||||
70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41,
|
||||
53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25,
|
||||
42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16},
|
||||
{70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41,
|
||||
76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40,
|
||||
42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28,
|
||||
38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90,
|
||||
24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60},
|
||||
{62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36,
|
||||
57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85,
|
||||
41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70,
|
||||
49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68,
|
||||
7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45},
|
||||
{65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67,
|
||||
58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48,
|
||||
60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13,
|
||||
96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61,
|
||||
90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94},
|
||||
{11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95,
|
||||
60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20,
|
||||
74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89,
|
||||
53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66,
|
||||
64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57},
|
||||
{43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41,
|
||||
34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85,
|
||||
99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95,
|
||||
17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76,
|
||||
16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69},
|
||||
{78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33,
|
||||
1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6,
|
||||
77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7,
|
||||
25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51,
|
||||
37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26},
|
||||
{88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56,
|
||||
85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58,
|
||||
28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54,
|
||||
57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15,
|
||||
72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68},
|
||||
{90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92,
|
||||
99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72,
|
||||
74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17,
|
||||
36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48,
|
||||
79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59},
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t access_code_table_2[10][10] = {
|
||||
{4, 7, 8, 0, 9, 1, 3, 6, 2, 5},
|
||||
{5, 0, 4, 1, 3, 8, 9, 7, 6, 2},
|
||||
{0, 2, 7, 8, 9, 1, 6, 3, 5, 4},
|
||||
{5, 9, 8, 4, 1, 7, 0, 2, 3, 6},
|
||||
{7, 3, 0, 1, 8, 9, 6, 5, 2, 4},
|
||||
{7, 2, 4, 0, 1, 9, 6, 3, 5, 8},
|
||||
{4, 1, 6, 3, 5, 8, 0, 7, 2, 9},
|
||||
{6, 8, 4, 1, 5, 3, 7, 2, 9, 0},
|
||||
{4, 1, 8, 3, 7, 2, 0, 6, 5, 9},
|
||||
{7, 4, 5, 6, 2, 3, 9, 1, 8, 0}};
|
||||
|
||||
static void rotate_right(uint8_t* data, int n_bytes, int n_bits) {
|
||||
uint8_t prior = data[n_bytes - 1];
|
||||
for(int i = 0; i < n_bytes; i++) {
|
||||
uint8_t tmp = data[i];
|
||||
data[i] = (data[i] >> n_bits) | ((prior & ((1 << n_bits) - 1)) << (8 - n_bits));
|
||||
prior = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void decrypt_spad_0(const uint8_t* spad, uint8_t* decrypted) {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
decrypted[i] = s_box[N_TABLES][spad[i]];
|
||||
}
|
||||
|
||||
int count = (decrypted[15] >> 4) + 7;
|
||||
int table = decrypted[15] + ITERATION_ADD * count;
|
||||
|
||||
for(int iter = 0; iter < count; iter++) {
|
||||
table -= ITERATION_ADD;
|
||||
rotate_right(decrypted, 15, 5); // only the first 15 bytes
|
||||
for(int i = 0; i < 15; i++) {
|
||||
decrypted[i] = s_box[table % N_TABLES][decrypted[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t crc16(uint64_t data, int bits, uint16_t init, uint16_t poly) {
|
||||
uint16_t v = init;
|
||||
for(int i = 0; i < bits; ++i) {
|
||||
v = (v >> 1) ^ (((v ^ data) & 1ULL) ? poly : 0);
|
||||
data >>= 1;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_access_code_crc(const uint8_t ac[3], const uint8_t body[6], const uint8_t crc[5]) {
|
||||
uint64_t msg = 0;
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
if(ac[i] > 0xF) return false;
|
||||
msg = (msg << 4) | ac[i];
|
||||
}
|
||||
for(int i = 0; i < 6; ++i) {
|
||||
uint8_t v = body[i];
|
||||
if(v > 99) return false;
|
||||
msg = (msg << 4) | (v / 10);
|
||||
msg = (msg << 4) | (v % 10);
|
||||
}
|
||||
uint16_t calculated_crc = crc16(msg, 60, 0xFFFF, 0x8408);
|
||||
uint16_t expected_crc = crc[0] * 10000 + crc[1] * 1000 + crc[2] * 100 + crc[3] * 10 + crc[4];
|
||||
return (calculated_crc == expected_crc);
|
||||
}
|
||||
|
||||
static void parse_access_code(const uint8_t* access_code, FuriString* parsed_data) {
|
||||
furi_assert(access_code);
|
||||
furi_assert(parsed_data);
|
||||
|
||||
uint8_t decrypted[6];
|
||||
// decrypted contains the decoded serial bytes (as 6 BCD bytes)
|
||||
for(int i = 0, j = 3; i < 6; ++i, j += 2) {
|
||||
decrypted[i] = (access_code[j]) * 10 + (access_code[j + 1]);
|
||||
}
|
||||
|
||||
uint8_t crc[5] = {0};
|
||||
memcpy(crc, access_code + 15, 5);
|
||||
|
||||
int boxes1[6] = {
|
||||
(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]};
|
||||
|
||||
for(int n = 0; n < 6; ++n) {
|
||||
decrypted[n] = access_code_table_1[4][boxes1[n]][decrypted[n]];
|
||||
}
|
||||
|
||||
int rv = decrypted[0] / 10;
|
||||
|
||||
int boxes2[6] = {
|
||||
(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10};
|
||||
for(int n = 0; n < 6; ++n) {
|
||||
if(n == 0) {
|
||||
decrypted[n] = (decrypted[n] & 0xF0) |
|
||||
access_code_table_2[boxes2[n]][decrypted[n] & 0x0F];
|
||||
} else {
|
||||
decrypted[n] = access_code_table_1[rv][boxes2[n]][decrypted[n]];
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
parsed_data,
|
||||
"Decrypted serial number:\n%02d%02d%02d%02d%02d%02d\n",
|
||||
decrypted[0],
|
||||
decrypted[1],
|
||||
decrypted[2],
|
||||
decrypted[3],
|
||||
decrypted[4],
|
||||
decrypted[5]);
|
||||
|
||||
furi_string_cat_printf(
|
||||
parsed_data,
|
||||
"CRC check: %s\n",
|
||||
check_access_code_crc(access_code, decrypted, crc) ? "Passed" : "Invalid");
|
||||
}
|
||||
|
||||
bool aic_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
furi_assert(device);
|
||||
furi_assert(parsed_data);
|
||||
bool parsed = false;
|
||||
|
||||
if(nfc_device_get_protocol(device) != NfcProtocolFelica) return false;
|
||||
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
const uint8_t ic_type = data->pmm.data[1];
|
||||
if(ic_type != 0xF0 && ic_type != 0xF1) {
|
||||
// Must be Felica Lite (0xF0) or Lite-S (0xF1) to parse
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t data_format_code_1 = data->data.fs.id.data[8];
|
||||
if(data_format_code_1 != 0) {
|
||||
// We only know Data Format Code {0x00, 0xXX}
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
furi_string_printf(parsed_data, "\e#Amusement IC Card\n");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
furi_string_cat_str(parsed_data, "\nType:\n");
|
||||
|
||||
// Determine card brand and type
|
||||
const uint8_t data_format_code_2 = data->data.fs.id.data[9];
|
||||
switch(data_format_code_2) {
|
||||
case 0x2A:
|
||||
furi_string_cat_str(parsed_data, "Bandai Namco Passport\n");
|
||||
break;
|
||||
case 0x3A:
|
||||
furi_string_cat_str(parsed_data, "Bandai Namco Passport (Old)\n");
|
||||
break;
|
||||
case 0x68:
|
||||
furi_string_cat_str(parsed_data, "Konami e-amusement pass\n");
|
||||
break;
|
||||
case 0x78:
|
||||
furi_string_cat_str(parsed_data, "SEGA Aime\n");
|
||||
break;
|
||||
case 0x79:
|
||||
furi_string_cat_str(parsed_data, "Taito NESiCA\n");
|
||||
break;
|
||||
default:
|
||||
parsed = false;
|
||||
return parsed; // Unknown vendor
|
||||
}
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
|
||||
// decrypt_spad_0 S-PAD 0
|
||||
uint8_t decrypted[16] = {0};
|
||||
decrypt_spad_0(data->data.fs.spad[0].data, decrypted);
|
||||
|
||||
// Get Access Code
|
||||
uint8_t access_code[20] = {0};
|
||||
for(int i = 0; i < 10; i++) {
|
||||
access_code[i * 2] = (decrypted[i + 6] & 0xF0) >> 4; // Get upper nibble
|
||||
access_code[i * 2 + 1] = decrypted[i + 6] & 0x0F; // Get lower nibble
|
||||
}
|
||||
furi_string_cat_str(parsed_data, "\nAccess Code:\n");
|
||||
bool access_code_is_bcd = true;
|
||||
for(int i = 0; i < 20; i++) {
|
||||
furi_string_cat_printf(parsed_data, "%d", access_code[i]);
|
||||
if(i % 4 == 3) {
|
||||
furi_string_cat_str(parsed_data, " ");
|
||||
}
|
||||
if(access_code[i] > 9) access_code_is_bcd = false;
|
||||
}
|
||||
furi_string_cat_str(parsed_data, "\n");
|
||||
|
||||
furi_string_cat_printf(parsed_data, "BCD valid: %s\n", access_code_is_bcd ? "Yes" : "No");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
|
||||
// Parse Access Code
|
||||
if(access_code_is_bcd && access_code[0] == 5) {
|
||||
furi_string_cat_str(parsed_data, "\n");
|
||||
parse_access_code(access_code, parsed_data);
|
||||
} else {
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\nAccess code preamble wrong: expected 5, got %d\n", access_code[0]);
|
||||
}
|
||||
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
furi_string_cat_str(parsed_data, "\nDecrypted S-PAD 0:\n");
|
||||
for(int i = 0; i < 16; i++) {
|
||||
furi_string_cat_printf(parsed_data, "%02X ", decrypted[i]);
|
||||
if(i == 7) {
|
||||
furi_string_cat_str(parsed_data, "\n");
|
||||
}
|
||||
}
|
||||
furi_string_cat_str(parsed_data, "\n");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const NfcSupportedCardsPlugin aic_plugin = {
|
||||
.protocol = NfcProtocolFelica,
|
||||
.verify = NULL,
|
||||
.read = NULL,
|
||||
.parse = aic_parse,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor aic_plugin_descriptor = {
|
||||
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||
.entry_point = &aic_plugin,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* aic_plugin_ep(void) {
|
||||
return &aic_plugin_descriptor;
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
#include <nfc/nfc_device.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
|
||||
#define TAG "Banapass"
|
||||
|
||||
static const uint64_t banapass_key_b_value_block = 0x019761AA8082;
|
||||
static const uint64_t banapass_key_b_access_code = 0x574343467632;
|
||||
typedef struct {
|
||||
uint64_t a;
|
||||
uint64_t b;
|
||||
} MfClassicKeyPair;
|
||||
|
||||
static MfClassicKeyPair banapass_keys_if_value_block[] = {
|
||||
{.a = 0x6090D00632F5, .b = 0x019761AA8082},
|
||||
{.a = 0xA99164400748, .b = 0x62742819AD7C},
|
||||
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
|
||||
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
|
||||
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
|
||||
{.a = 0x30424C029001, .b = 0x024E4E44001F},
|
||||
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
|
||||
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
|
||||
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
|
||||
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
|
||||
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
|
||||
{.a = 0x730050555253, .b = 0x4146D4A956C4},
|
||||
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
|
||||
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
|
||||
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
|
||||
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
|
||||
};
|
||||
|
||||
static MfClassicKeyPair banapass_keys_if_access_code[] = {
|
||||
{.a = 0x6090D00632F5, .b = 0x574343467632},
|
||||
{.a = 0xA99164400748, .b = 0x62742819AD7C},
|
||||
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
|
||||
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
|
||||
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
|
||||
{.a = 0x30424C029001, .b = 0x024E4E44001F},
|
||||
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
|
||||
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
|
||||
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
|
||||
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
|
||||
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
|
||||
{.a = 0x730050555253, .b = 0x4146D4A956C4},
|
||||
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
|
||||
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
|
||||
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
|
||||
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
|
||||
};
|
||||
|
||||
static bool banapass_verify(Nfc* nfc) {
|
||||
bool verified = true;
|
||||
|
||||
const uint8_t verify_sector = 0;
|
||||
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
|
||||
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
|
||||
|
||||
MfClassicKey key_a_0 = {};
|
||||
bit_lib_num_to_bytes_be(
|
||||
banapass_keys_if_value_block[0].a, COUNT_OF(key_a_0.data), key_a_0.data);
|
||||
|
||||
MfClassicAuthContext auth_ctx = {};
|
||||
MfClassicError error =
|
||||
mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);
|
||||
|
||||
if(error != MfClassicErrorNone) {
|
||||
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
|
||||
verified = false;
|
||||
}
|
||||
|
||||
return verified;
|
||||
}
|
||||
|
||||
static bool banapass_read(Nfc* nfc, NfcDevice* device) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(device);
|
||||
|
||||
bool is_read = false;
|
||||
|
||||
MfClassicData* data = mf_classic_alloc();
|
||||
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
|
||||
|
||||
do {
|
||||
MfClassicType type = MfClassicType1k;
|
||||
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
|
||||
if(error != MfClassicErrorNone) break;
|
||||
if(type != MfClassicType1k) {
|
||||
FURI_LOG_E(TAG, "Card not MIFARE Classic 1k");
|
||||
break;
|
||||
}
|
||||
|
||||
data->type = type;
|
||||
MfClassicDeviceKeys keys = {};
|
||||
|
||||
// Access Code Read Attempt
|
||||
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||
bit_lib_num_to_bytes_be(
|
||||
banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
|
||||
FURI_BIT_SET(keys.key_a_mask, i);
|
||||
bit_lib_num_to_bytes_be(
|
||||
banapass_keys_if_access_code[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
|
||||
FURI_BIT_SET(keys.key_b_mask, i);
|
||||
}
|
||||
|
||||
MfClassicError error_access_code = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||
|
||||
if(error_access_code == MfClassicErrorNone) {
|
||||
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
||||
is_read = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Value Block Read Attempt
|
||||
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||
bit_lib_num_to_bytes_be(
|
||||
banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
|
||||
FURI_BIT_SET(keys.key_a_mask, i);
|
||||
bit_lib_num_to_bytes_be(
|
||||
banapass_keys_if_value_block[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
|
||||
FURI_BIT_SET(keys.key_b_mask, i);
|
||||
}
|
||||
|
||||
MfClassicError error_value_block = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||
|
||||
if(error_value_block == MfClassicErrorNone) {
|
||||
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
||||
is_read = true;
|
||||
break;
|
||||
}
|
||||
FURI_LOG_E(TAG, "Failed to read data. Bad keys?");
|
||||
} while(false);
|
||||
|
||||
mf_classic_free(data);
|
||||
|
||||
return is_read;
|
||||
}
|
||||
|
||||
static bool banapass_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
furi_assert(device);
|
||||
|
||||
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
do {
|
||||
// verify key
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
|
||||
uint64_t key_a = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
|
||||
uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
|
||||
if(key_a != banapass_keys_if_value_block[0].a) break;
|
||||
|
||||
furi_string_set_str(parsed_data, "\e#Banapass\n");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
furi_string_cat_str(parsed_data, "\nBandai Namco Passport\n");
|
||||
|
||||
// banapass Magic is stored at block 1, byte 2-7
|
||||
uint8_t magic_bytes[6];
|
||||
for(int i = 0; i < 6; i++) {
|
||||
magic_bytes[i] = data->block[1].data[2 + i];
|
||||
}
|
||||
|
||||
// verify banapass magic
|
||||
if(magic_bytes[0] != 'N' || magic_bytes[1] != 'B' || magic_bytes[2] != 'G' ||
|
||||
magic_bytes[3] != 'I' || magic_bytes[4] != 'C')
|
||||
break;
|
||||
|
||||
// banapass checksum is stored at block 1, starts from byte 8-15
|
||||
uint8_t check_sum[8];
|
||||
for(int i = 0; i < 8; i++) {
|
||||
check_sum[i] = data->block[1].data[8 + i];
|
||||
}
|
||||
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
|
||||
bool is_block_2_null = true;
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if(data->block[2].data[i] != 0) {
|
||||
is_block_2_null = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_block_2_null) {
|
||||
furi_string_cat_str(
|
||||
parsed_data,
|
||||
"\nPlease scan the clone at the\nnearest CHUNITHM or\nmaimai Cabinet for the\nAccess Code.\n");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
} else {
|
||||
switch(key_b) {
|
||||
case banapass_key_b_value_block:
|
||||
int32_t value = 0;
|
||||
uint8_t addr = 0;
|
||||
bool value_found = mf_classic_block_to_value(
|
||||
&data->block[2], &value, &addr); // block 2 is value block
|
||||
if(value_found) {
|
||||
furi_string_cat_printf(parsed_data, "\nValue: %08lX", value);
|
||||
} else {
|
||||
furi_string_cat_str(parsed_data, "\nPotential clone:\nInvalid value block.");
|
||||
}
|
||||
furi_string_cat_str(
|
||||
parsed_data,
|
||||
"\nPlease check the back of\nyour Bandai Namco Passport\nfor the Access Code.\n");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
break;
|
||||
|
||||
case banapass_key_b_access_code:
|
||||
// banapass access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes
|
||||
uint8_t access_code[10];
|
||||
|
||||
furi_string_cat_printf(parsed_data, "\nAccess Code:\n");
|
||||
bool access_code_is_bcd = true;
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
access_code[i] = data->block[2].data[6 + i];
|
||||
furi_string_cat_printf(parsed_data, "%02X", access_code[i]);
|
||||
if(i % 2 == 1) {
|
||||
furi_string_cat_str(parsed_data, " ");
|
||||
}
|
||||
if((access_code[i] >> 4) > 9) access_code_is_bcd = false;
|
||||
if((access_code[i] & 0x0F) > 9) access_code_is_bcd = false;
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\nBCD valid: %s\n", access_code_is_bcd ? "Yes" : "No");
|
||||
if((access_code[0] >> 4) != 3) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data,
|
||||
"Potential clone:\nAccess Code preamble\nexpected 3, got %d\n",
|
||||
(access_code[0] >> 4));
|
||||
}
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_string_cat_str(parsed_data, "\nMagic:\n");
|
||||
for(int i = 0; i < 6; i++) {
|
||||
furi_string_cat_printf(parsed_data, "%c", magic_bytes[i]);
|
||||
}
|
||||
furi_string_cat_str(parsed_data, "\nChecksum:\n");
|
||||
for(int i = 0; i < 8; i++) {
|
||||
furi_string_cat_printf(parsed_data, "%02X ", check_sum[i]);
|
||||
}
|
||||
|
||||
furi_string_cat_str(parsed_data, "\n");
|
||||
furi_string_cat_str(
|
||||
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
|
||||
|
||||
parsed = true;
|
||||
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const NfcSupportedCardsPlugin banapass_plugin = {
|
||||
.protocol = NfcProtocolMfClassic,
|
||||
.verify = banapass_verify,
|
||||
.read = banapass_read,
|
||||
.parse = banapass_parse,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor banapass_plugin_descriptor = {
|
||||
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||
.entry_point = &banapass_plugin,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* banapass_plugin_ep(void) {
|
||||
return &banapass_plugin_descriptor;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
Filetype: NFC Vendors
|
||||
Version: 1
|
||||
# Please do not change IDs in this list. Add new to the end if necessary.
|
||||
# ID: "Vendor Country"
|
||||
1: Motorola UK
|
||||
2: ST Microelectronics SA France
|
||||
3: Hitachi, Ltd Japan
|
||||
4: NXP Semiconductors Germany
|
||||
5: Infineon Technologies AG Germany
|
||||
6: Cylink USA
|
||||
7: Texas Instrument France
|
||||
8: Fujitsu Limited Japan
|
||||
9: Matsushita Electronics Corporation, Semiconductor Company Japan
|
||||
10: NEC Japan
|
||||
11: Oki Electric Industry Co. Ltd Japan
|
||||
12: Toshiba Corp. Japan
|
||||
13: Mitsubishi Electric Corp. Japan
|
||||
14: Samsung Electronics Co. Ltd Korea
|
||||
15: Hynix / Hyundai, Korea
|
||||
16: LG-Semiconductors Co. Ltd Korea
|
||||
17: Emosyn-EM Microelectronics USA
|
||||
18: INSIDE Technology France
|
||||
19: ORGA Kartensysteme GmbH Germany
|
||||
20: SHARP Corporation Japan
|
||||
21: ATMEL France
|
||||
22: EM Microelectronic-Marin SA Switzerland
|
||||
23: KSW Microtec GmbH Germany
|
||||
24: ZMD AG Germany
|
||||
25: XICOR, Inc. USA
|
||||
26: Sony Corporation Japan
|
||||
27: Malaysia Microelectronic Solutions Sdn. Bhd Malaysia
|
||||
28: Emosyn USA
|
||||
29: Shanghai Fudan Microelectronics Co. Ltd. P.R. China
|
||||
30: Magellan Technology Pty Limited Australia
|
||||
31: Melexis NV BO Switzerland
|
||||
32: Renesas Technology Corp. Japan
|
||||
33: TAGSYS France
|
||||
34: Transcore USA
|
||||
35: Shanghai belling corp., ltd. China
|
||||
36: Masktech Germany Gmbh Germany
|
||||
37: Innovision Research and Technology Plc UK
|
||||
38: Hitachi ULSI Systems Co., Ltd. Japan
|
||||
39: Cypak AB Sweden
|
||||
40: Ricoh Japan
|
||||
41: ASK France
|
||||
42: Unicore Microsystems, LLC Russian Federation
|
||||
43: Dallas Semiconductor/Maxim USA
|
||||
44: Impinj, Inc. USA
|
||||
45: RightPlug Alliance USA
|
||||
46: Broadcom Corporation USA
|
||||
47: MStar Semiconductor, Inc Taiwan, ROC
|
||||
48: BeeDar Technology Inc. USA
|
||||
49: RFIDsec Denmark
|
||||
50: Schweizer Electronic AG Germany
|
||||
51: AMIC Technology Corp Taiwan
|
||||
52: Mikron JSC Russia
|
||||
53: Fraunhofer Institute for Photonic Microsystems Germany
|
||||
54: IDS Microchip AG Switzerland
|
||||
55: Thinfilm - Kovio USA
|
||||
56: HMT Microelectronic Ltd Switzerland
|
||||
57: Silicon Craft Technology Thailand
|
||||
58: Advanced Film Device Inc. Japan
|
||||
59: Nitecrest Ltd UK
|
||||
60: Verayo Inc. USA
|
||||
61: HID Global USA
|
||||
62: Productivity Engineering Gmbh Germany
|
||||
63: Austriamicrosystems AG (reserved) Austria
|
||||
64: Gemalto SA France
|
||||
65: Renesas Electronics Corporation Japan
|
||||
66: 3Alogics Inc Korea
|
||||
67: Top TroniQ Asia Limited Hong Kong
|
||||
68: Gentag Inc. USA
|
||||
69: Invengo Information Technology Co.Ltd China
|
||||
70: Guangzhou Sysur Microelectronics, Inc China
|
||||
71: CEITEC S.A. Brazil
|
||||
72: Shanghai Quanray Electronics Co. Ltd. China
|
||||
73: MediaTek Inc Taiwan
|
||||
74: Angstrem PJSC Russia
|
||||
75: Celisic Semiconductor (Hong Kong) Limited China
|
||||
76: LEGIC Identsystems AG Switzerland
|
||||
77: Balluff GmbH Germany
|
||||
78: Oberthur Technologies France
|
||||
79: Silterra Malaysia Sdn. Bhd. Malaysia
|
||||
80: DELTA Danish Electronics, Light & Acoustics Denmark
|
||||
81: Giesecke & Devrient GmbH Germany
|
||||
82: Shenzhen China Vision Microelectronics Co., Ltd. China
|
||||
83: Shanghai Feiju Microelectronics Co. Ltd. China
|
||||
84: Intel Corporation USA
|
||||
85: Microsensys GmbH Germany
|
||||
86: Sonix Technology Co., Ltd. Taiwan
|
||||
87: Qualcomm Technologies Inc USA
|
||||
88: Realtek Semiconductor Corp Taiwan
|
||||
89: Freevision Technologies Co. Ltd China
|
||||
90: Giantec Semiconductor Inc. China
|
||||
91: JSC Angstrem-T Russia
|
||||
92: STARCHIP France
|
||||
93: SPIRTECH France
|
||||
94: GANTNER Electronic GmbH Austria
|
||||
95: Nordic Semiconductor Norway
|
||||
96: Verisiti Inc USA
|
||||
97: Wearlinks Technology Inc. China
|
||||
98: Userstar Information Systems Co., Ltd Taiwan
|
||||
99: Pragmatic Printing Ltd. UK
|
||||
100: Associacao do Laboratorio de Sistemas Integraveis Tecnologico - LSI-TEC Brazil
|
||||
101: Tendyron Corporation China
|
||||
102: MUTO Smart Co., Ltd. Korea
|
||||
103: ON Semiconductor USA
|
||||
104: TUBITAK BILGEM Turkey
|
||||
105: Huada Semiconductor Co., Ltd China
|
||||
106: SEVENEY France
|
||||
107: ISSM France
|
||||
108: Wisesec Ltd Israel
|
||||
124: DB HiTek Co Ltd Korea
|
||||
125: SATO Vicinity Australia
|
||||
126: Holtek Taiwan
|
||||
@@ -69,4 +69,6 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput)
|
||||
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
|
||||
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
|
||||
|
||||
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
|
||||
|
||||
ADD_SCENE(nfc, generate_info, GenerateInfo)
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
|
||||
#include "../helpers/protocol_support/felica/felica_render.h"
|
||||
|
||||
enum {
|
||||
FelicaMoreInfoStateMenu,
|
||||
FelicaMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index
|
||||
};
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexDirectory,
|
||||
SubmenuIndexDynamic, // dynamic indices start here
|
||||
};
|
||||
|
||||
void nfc_scene_felica_more_info_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
const uint32_t state =
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
||||
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Directory",
|
||||
SubmenuIndexDirectory,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
nfc);
|
||||
|
||||
FuriString* label = furi_string_alloc();
|
||||
|
||||
switch(data->workflow_type) {
|
||||
case FelicaLite:
|
||||
furi_string_printf(label, "All blocks");
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(label),
|
||||
SubmenuIndexDynamic,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
nfc);
|
||||
break;
|
||||
case FelicaStandard:
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) {
|
||||
const FelicaService* service = simple_array_cget(data->services, i);
|
||||
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
|
||||
if(!is_public) {
|
||||
continue;
|
||||
}
|
||||
furi_string_printf(label, "Readable serv %04X", service->code);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(label),
|
||||
i + SubmenuIndexDynamic,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
nfc);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_free(label);
|
||||
|
||||
if(state >= FelicaMoreInfoStateItem) {
|
||||
submenu_set_selected_item(
|
||||
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
const uint32_t state =
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
||||
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexDirectory) {
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_more_info_render_felica_dir(data, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager,
|
||||
NfcSceneFelicaMoreInfo,
|
||||
FelicaMoreInfoStateItem + SubmenuIndexDirectory);
|
||||
consumed = true;
|
||||
} else {
|
||||
const uint16_t service_ind = event.event - 1; // offset the three enums above
|
||||
|
||||
text_box_reset(nfc->text_box);
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
|
||||
switch(data->workflow_type) {
|
||||
case FelicaLite:
|
||||
nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store);
|
||||
break;
|
||||
case FelicaStandard:
|
||||
const FelicaService* service = simple_array_cget(data->services, service_ind);
|
||||
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
|
||||
nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code);
|
||||
break;
|
||||
default:
|
||||
furi_string_set_str(nfc->text_box_store, "IC type not implemented yet");
|
||||
break;
|
||||
}
|
||||
text_box_set_font(nfc->text_box, TextBoxFontHex);
|
||||
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state >= FelicaMoreInfoStateItem) {
|
||||
widget_reset(nfc->widget);
|
||||
text_box_reset(nfc->text_box);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
|
||||
} else {
|
||||
widget_reset(nfc->widget);
|
||||
text_box_reset(nfc->text_box);
|
||||
// Return directly to the Info scene
|
||||
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_more_info_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Clear views
|
||||
widget_reset(nfc->widget);
|
||||
text_box_reset(nfc->text_box);
|
||||
furi_string_reset(nfc->text_box_store);
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#define TAG "NfcMfUlCDictAttack"
|
||||
|
||||
// TODO: Support card_detected properly
|
||||
// TODO: Support card_detected properly -nofl
|
||||
|
||||
enum {
|
||||
DictAttackStateUserDictInProgress,
|
||||
|
||||
Reference in New Issue
Block a user