mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 04:41:26 +04:00
[FL-3569] NFC CLI commands (#4158)
* feat: FuriThread stdin * ci: fix f18 * feat: stdio callback context * feat: FuriPipe * POTENTIALLY EXPLOSIVE pipe welding * fix: non-explosive welding * Revert welding * docs: furi_pipe * feat: pipe event loop integration * update f18 sdk * f18 * docs: make doxygen happy * fix: event loop not triggering when pipe attached to stdio * fix: partial stdout in pipe * allow simultaneous in and out subscription in event loop * feat: vcp i/o * feat: cli ansi stuffs and history * feat: more line editing * working but slow cli rewrite * restore previous speed after 4 days of debugging 🥲 * fix: cli_app_should_stop * fix: cli and event_loop memory leaks * style: remove commented out code * ci: fix pvs warnings * fix: unit tests, event_loop crash * ci: fix build * ci: silence pvs warning * feat: cli gpio * ci: fix formatting * Fix memory leak during event loop unsubscription * Event better memory leak fix * feat: cli completions * Merge remote-tracking branch 'origin/dev' into portasynthinca3/3928-cli-threads * merge fixups * temporarily exclude speaker_debug app * pvs and unit tests fixups * feat: commands in fals * move commands out of flash, code cleanup * ci: fix errors * fix: run commands in buffer when stopping session * speedup cli file transfer * fix f18 * separate cli_shell into modules * fix pvs warning * fix qflipper refusing to connect * remove temp debug logs * remove erroneous conclusion * Fix memory leak during event loop unsubscription * Event better memory leak fix * unit test for the fix * improve thread stdio callback signatures * pipe stdout timeout * update api symbols * fix f18, formatting * fix pvs warnings * increase stack size, hope to fix unit tests * cli completions * more key combos * commands in fals * move commands out of flash * ci: fix errors * speedup cli file transfer * merge fixups * fix f18 * cli: revert flag changes * cli: fix formatting * cli, fbt: loopback perf benchmark * thread, event_loop: subscribing to thread flags * cli: signal internal events using thread flags, improve performance * fix f18, formatting * event_loop: fix crash * storage_cli: increase write_chunk buffer size again * cli: explanation for order=0 * thread, event_loop: thread flags callback refactor * cli: increase stack size * cli: rename cli_app_should_stop -> cli_is_pipe_broken_or_is_etx_next_char * cli: use plain array instead of mlib for history * cli: prepend file name to static fns * cli: fix formatting * cli_shell: increase stack size * Now cli_shell can be customized with another motd and another command set * Added custom motd callback definition * Now user can alloc and free his own cli command set * cli_vcp can now restart shell with another command set * Help command modified to show available commands from different command sets * Api adjustement * Reworked nfc_cli to start new shell with another command set * Revert custom shell changes from vcp * Custom motd callback moved to cli_shell * Cli Shell now can be started from ongoing cli command * Help command moved to a separate function so it can be used for custom shell * Now nfc command spawns separate shell for further nfc commands * cli_shell: give up pipe to command thread * fix formatting * cli_shell: separate into toolbox * speaker_debug: fix * fix: format * Merge branch 'portasynthinca3/3928-3929-cli-fals-threads' into portasynthinca3/3965-cli_shell-toolbox * fix merge * fix. merge. * fix formatting * fix: cmd flags * fix: formatting * Added basic command descriptor structs and macros * Basic nfc commands definitions added * Nfc cli commands collection and functions added * Raw skeleton of nfc cli processor added * cli: increase default stack depth * New callbacks for ctx alloc / free added * nfc_cli moved to cli folder * Some more logic for command processor * Scanner command no works via command_processor * plugin manifest adj * Argument descriptors were removed, now only keys left * Some helper command function implemented * Command processor logic now mostly works * Added all parsers and dummy implementation of raw cmd * Now processor checks duplicated keys and treat them as errors * Some renamings * Arguments processing moved to separate function * Now command processor can reuse context of previuos command for the next one if it's allowed * can_reuse callback added for checking if context can be reused * command processor is now freed on nfc cli exit * Some cleanups * First working version of raw command * Now input data are placed directly to bit buffer * Added tag * Introduced request/response structs * Moved raw command to a separate folder * Moved some common types to header * Added protocol specific handlers for iso14a and felica * Opened felica crc header for referencing * Added handler for iso14443_3b * Opened iso15693_3_poller for referencing * Added iso15693_3 handler for raw command * NfcCliRawError enum introduced for response result * Refactored handlers implementation * Formatting functions now added as helpers * New printing result logic * Not present error value added to enum * Timeout added to raw command * Command processor now supports multivalue keys * Apdu command implementation added * NfcScanner moved to helpers and command now uses it * Helper now can format protocol names * Dump command added * Added some more functions to scanner helper * Dump main logic simplified * Dump handlers moved to protocols folder * Protocol parser added to simplify searching protocol by name * Protocol and key arguments added to dump command * Cleanups * Apdu now parses protocol using helper parser * Raw now parses protocol using helper parser * Wrong naming fix * Emulate command added to cli * Description added to action descriptor and command macros * Description field added to all commands * Removed unnecessary enum for commands * Added functions for formatting command and action info * Proper error messages and help added * Fix for unsupported single action command * Function renamed to more appropriate * Field command moved to all other commands * Cleanups * Nfc commands modified with new cli shell * Removed previous nfc_cli.c after merge * Removed nfc_cli.h header * Some renamings and cleanups * Some comments and instructions added * Some comments and instructions added * TODOs removed * Fix for missing parse callback * Added not implemented dummy for mfu actions, for now * Fix name mismatch * Remove unneeded header * Mfu command moved to separate folder, also raw info action logic added * Dictionary with id/vendors added to assets. It is used by nfc_cli_mfu_info_get_vendor function * One more unneeded header removed * Moved mfu info action to a separate file * Info action now uses sync mfu poller * mfu rdbl action added * wrbl action added for mfu command * Some formatting for rdbl command * Function for formatting mfu errors added * All mfu actions now show errors in the same way * Fix error with sync poller. Previously when read failed function returned ErrorNone, now it processes iso14a error to get proper value * Make PVS happy * Nfc cli now doesn't start if desktop app is running * Make action description look more common * Scanner now has -t key and can show detected protocol hierarchies * Apdu now checks max input payload data * Proper format * Proper error handling added to dump command * Timeout key added dump command * Fix merge issue * formatting * Pragma pack replaced with FURI_PACKED * Fix felica memory leak --------- Co-authored-by: Anna Antonenko <portasynthinca3@gmail.com> Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com> Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: hedger <hedger@nanode.su>
This commit is contained in:
@@ -8,11 +8,7 @@ App(
|
|||||||
stack_size=5 * 1024,
|
stack_size=5 * 1024,
|
||||||
order=30,
|
order=30,
|
||||||
resources="resources",
|
resources="resources",
|
||||||
sources=[
|
sources=["*.c*", "!plugins", "!nfc_cli.c", "!cli"],
|
||||||
"*.c*",
|
|
||||||
"!plugins",
|
|
||||||
"!nfc_cli.c",
|
|
||||||
],
|
|
||||||
fap_libs=["assets", "mbedtls"],
|
fap_libs=["assets", "mbedtls"],
|
||||||
fap_icon="icon.png",
|
fap_icon="icon.png",
|
||||||
fap_category="NFC",
|
fap_category="NFC",
|
||||||
@@ -263,7 +259,46 @@ App(
|
|||||||
apptype=FlipperAppType.PLUGIN,
|
apptype=FlipperAppType.PLUGIN,
|
||||||
entry_point="cli_nfc_ep",
|
entry_point="cli_nfc_ep",
|
||||||
requires=["cli"],
|
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(
|
App(
|
||||||
|
|||||||
311
applications/main/nfc/cli/commands/apdu/nfc_cli_command_apdu.c
Normal file
311
applications/main/nfc/cli/commands/apdu/nfc_cli_command_apdu.c
Normal file
@@ -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;
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../nfc_cli_apdu_common_types.h"
|
||||||
|
|
||||||
|
NfcCommand
|
||||||
|
nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../nfc_cli_apdu_common_types.h"
|
||||||
|
|
||||||
|
NfcCommand
|
||||||
|
nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
319
applications/main/nfc/cli/commands/dump/nfc_cli_command_dump.c
Normal file
319
applications/main/nfc/cli/commands/dump/nfc_cli_command_dump.c
Normal file
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
61
applications/main/nfc/cli/commands/helpers/nfc_cli_format.c
Normal file
61
applications/main/nfc/cli/commands/helpers/nfc_cli_format.c
Normal file
@@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
applications/main/nfc/cli/commands/helpers/nfc_cli_format.h
Normal file
16
applications/main/nfc/cli/commands/helpers/nfc_cli_format.h
Normal file
@@ -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);
|
||||||
96
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.c
Normal file
96
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
21
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.h
Normal file
21
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.h
Normal file
@@ -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);
|
||||||
227
applications/main/nfc/cli/commands/mfu/nfc_cli_action_info.c
Normal file
227
applications/main/nfc/cli/commands/mfu/nfc_cli_action_info.c
Normal file
@@ -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);
|
||||||
52
applications/main/nfc/cli/commands/mfu/nfc_cli_action_rdbl.c
Normal file
52
applications/main/nfc/cli/commands/mfu/nfc_cli_action_rdbl.c
Normal file
@@ -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);
|
||||||
71
applications/main/nfc/cli/commands/mfu/nfc_cli_action_wrbl.c
Normal file
71
applications/main/nfc/cli/commands/mfu/nfc_cli_action_wrbl.c
Normal file
@@ -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);
|
||||||
77
applications/main/nfc/cli/commands/mfu/nfc_cli_command_mfu.c
Normal file
77
applications/main/nfc/cli/commands/mfu/nfc_cli_command_mfu.c
Normal file
@@ -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;
|
||||||
126
applications/main/nfc/cli/commands/nfc_cli_command_emulate.c
Normal file
126
applications/main/nfc/cli/commands/nfc_cli_command_emulate.c
Normal file
@@ -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;
|
||||||
28
applications/main/nfc/cli/commands/nfc_cli_command_field.c
Normal file
28
applications/main/nfc/cli/commands/nfc_cli_command_field.c
Normal file
@@ -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;
|
||||||
97
applications/main/nfc/cli/commands/nfc_cli_command_scanner.c
Normal file
97
applications/main/nfc/cli/commands/nfc_cli_command_scanner.c
Normal file
@@ -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;
|
||||||
352
applications/main/nfc/cli/commands/raw/nfc_cli_command_raw.c
Normal file
352
applications/main/nfc/cli/commands/raw/nfc_cli_command_raw.c
Normal file
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
124
applications/main/nfc/cli/nfc_cli.c
Normal file
124
applications/main/nfc/cli/nfc_cli.c
Normal file
@@ -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);
|
||||||
69
applications/main/nfc/cli/nfc_cli_command_base.h
Normal file
69
applications/main/nfc/cli/nfc_cli_command_base.h
Normal file
@@ -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;
|
||||||
130
applications/main/nfc/cli/nfc_cli_command_base_i.h
Normal file
130
applications/main/nfc/cli/nfc_cli_command_base_i.h
Normal file
@@ -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); \
|
||||||
|
}
|
||||||
388
applications/main/nfc/cli/nfc_cli_command_processor.c
Normal file
388
applications/main/nfc/cli/nfc_cli_command_processor.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
15
applications/main/nfc/cli/nfc_cli_command_processor.h
Normal file
15
applications/main/nfc/cli/nfc_cli_command_processor.h
Normal file
@@ -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);
|
||||||
161
applications/main/nfc/cli/nfc_cli_commands.c
Normal file
161
applications/main/nfc/cli/nfc_cli_commands.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
applications/main/nfc/cli/nfc_cli_commands.h
Normal file
27
applications/main/nfc/cli/nfc_cli_commands.h
Normal file
@@ -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);
|
||||||
@@ -1,67 +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 <toolbox/pipe.h>
|
|
||||||
|
|
||||||
#include <furi_hal_nfc.h>
|
|
||||||
|
|
||||||
#define FLAG_EVENT (1 << 10)
|
|
||||||
|
|
||||||
static void nfc_cli_print_usage(void) {
|
|
||||||
printf("Usage:\r\n");
|
|
||||||
printf("nfc <cmd>\r\n");
|
|
||||||
printf("Cmd list:\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 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_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);
|
|
||||||
115
applications/main/nfc/resources/nfc/assets/vendors.nfc
Normal file
115
applications/main/nfc/resources/nfc/assets/vendors.nfc
Normal file
@@ -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
|
||||||
@@ -15,7 +15,6 @@ typedef struct CliVcp CliVcp;
|
|||||||
|
|
||||||
void cli_vcp_enable(CliVcp* cli_vcp);
|
void cli_vcp_enable(CliVcp* cli_vcp);
|
||||||
void cli_vcp_disable(CliVcp* cli_vcp);
|
void cli_vcp_disable(CliVcp* cli_vcp);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ env.Append(
|
|||||||
File("protocols/iso14443_3b/iso14443_3b_poller.h"),
|
File("protocols/iso14443_3b/iso14443_3b_poller.h"),
|
||||||
File("protocols/iso14443_4a/iso14443_4a_poller.h"),
|
File("protocols/iso14443_4a/iso14443_4a_poller.h"),
|
||||||
File("protocols/iso14443_4b/iso14443_4b_poller.h"),
|
File("protocols/iso14443_4b/iso14443_4b_poller.h"),
|
||||||
|
File("protocols/iso15693_3/iso15693_3_poller.h"),
|
||||||
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
|
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
|
||||||
File("protocols/mf_classic/mf_classic_poller.h"),
|
File("protocols/mf_classic/mf_classic_poller.h"),
|
||||||
File("protocols/mf_plus/mf_plus_poller.h"),
|
File("protocols/mf_plus/mf_plus_poller.h"),
|
||||||
@@ -52,6 +53,7 @@ env.Append(
|
|||||||
File("protocols/felica/felica_poller_sync.h"),
|
File("protocols/felica/felica_poller_sync.h"),
|
||||||
# Misc
|
# Misc
|
||||||
File("helpers/nfc_util.h"),
|
File("helpers/nfc_util.h"),
|
||||||
|
File("helpers/felica_crc.h"),
|
||||||
File("helpers/iso14443_crc.h"),
|
File("helpers/iso14443_crc.h"),
|
||||||
File("helpers/iso13239_crc.h"),
|
File("helpers/iso13239_crc.h"),
|
||||||
File("helpers/nfc_data_generator.h"),
|
File("helpers/nfc_data_generator.h"),
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
furi_string_free(temp_str);
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|||||||
@@ -155,15 +155,13 @@ typedef struct {
|
|||||||
FelicaFSUnion data;
|
FelicaFSUnion data;
|
||||||
} FelicaData;
|
} FelicaData;
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
typedef struct FURI_PACKED {
|
||||||
typedef struct {
|
|
||||||
uint8_t code;
|
uint8_t code;
|
||||||
FelicaIDm idm;
|
FelicaIDm idm;
|
||||||
uint8_t service_num;
|
uint8_t service_num;
|
||||||
uint16_t service_code;
|
uint16_t service_code;
|
||||||
uint8_t block_count;
|
uint8_t block_count;
|
||||||
} FelicaCommandHeader;
|
} FelicaCommandHeader;
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t length;
|
uint8_t length;
|
||||||
|
|||||||
@@ -248,7 +248,8 @@ static NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void
|
|||||||
poller_context->error = MfUltralightErrorNone;
|
poller_context->error = MfUltralightErrorNone;
|
||||||
command = NfcCommandStop;
|
command = NfcCommandStop;
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeReadFailed) {
|
} else if(mfu_event->type == MfUltralightPollerEventTypeReadFailed) {
|
||||||
poller_context->error = mfu_event->data->error;
|
poller_context->error = mf_ultralight_process_error(
|
||||||
|
mfu_poller->iso14443_3a_poller->iso14443_3a_event_data.error);
|
||||||
command = NfcCommandStop;
|
command = NfcCommandStop;
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
} else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
||||||
if(poller_context->auth_context != NULL) {
|
if(poller_context->auth_context != NULL) {
|
||||||
|
|||||||
@@ -442,7 +442,6 @@ static int32_t cli_shell_thread(void* context) {
|
|||||||
// ==========
|
// ==========
|
||||||
// Public API
|
// Public API
|
||||||
// ==========
|
// ==========
|
||||||
|
|
||||||
CliShell* cli_shell_alloc(
|
CliShell* cli_shell_alloc(
|
||||||
CliShellMotd motd,
|
CliShellMotd motd,
|
||||||
void* context,
|
void* context,
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ Header,+,lib/nanopb/pb.h,,
|
|||||||
Header,+,lib/nanopb/pb_decode.h,,
|
Header,+,lib/nanopb/pb_decode.h,,
|
||||||
Header,+,lib/nanopb/pb_encode.h,,
|
Header,+,lib/nanopb/pb_encode.h,,
|
||||||
Header,+,lib/nfc/helpers/crypto1.h,,
|
Header,+,lib/nfc/helpers/crypto1.h,,
|
||||||
|
Header,+,lib/nfc/helpers/felica_crc.h,,
|
||||||
Header,+,lib/nfc/helpers/iso13239_crc.h,,
|
Header,+,lib/nfc/helpers/iso13239_crc.h,,
|
||||||
Header,+,lib/nfc/helpers/iso14443_crc.h,,
|
Header,+,lib/nfc/helpers/iso14443_crc.h,,
|
||||||
Header,+,lib/nfc/helpers/nfc_data_generator.h,,
|
Header,+,lib/nfc/helpers/nfc_data_generator.h,,
|
||||||
@@ -153,6 +154,7 @@ Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h,,
|
|||||||
Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h,,
|
Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h,,
|
||||||
Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b.h,,
|
Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b.h,,
|
||||||
Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h,,
|
Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h,,
|
||||||
|
Header,+,lib/nfc/protocols/iso15693_3/iso15693_3_poller.h,,
|
||||||
Header,+,lib/nfc/protocols/mf_classic/mf_classic.h,,
|
Header,+,lib/nfc/protocols/mf_classic/mf_classic.h,,
|
||||||
Header,+,lib/nfc/protocols/mf_classic/mf_classic_listener.h,,
|
Header,+,lib/nfc/protocols/mf_classic/mf_classic_listener.h,,
|
||||||
Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,,
|
Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,,
|
||||||
@@ -1059,6 +1061,9 @@ Function,+,felica_calculate_mac_write,void,"mbedtls_des3_context*, const uint8_t
|
|||||||
Function,+,felica_calculate_session_key,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, uint8_t*"
|
Function,+,felica_calculate_session_key,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, uint8_t*"
|
||||||
Function,+,felica_check_mac,_Bool,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*"
|
Function,+,felica_check_mac,_Bool,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*"
|
||||||
Function,+,felica_copy,void,"FelicaData*, const FelicaData*"
|
Function,+,felica_copy,void,"FelicaData*, const FelicaData*"
|
||||||
|
Function,+,felica_crc_append,void,BitBuffer*
|
||||||
|
Function,+,felica_crc_check,_Bool,const BitBuffer*
|
||||||
|
Function,+,felica_crc_trim,void,BitBuffer*
|
||||||
Function,+,felica_free,void,FelicaData*
|
Function,+,felica_free,void,FelicaData*
|
||||||
Function,+,felica_get_base_data,FelicaData*,const FelicaData*
|
Function,+,felica_get_base_data,FelicaData*,const FelicaData*
|
||||||
Function,+,felica_get_device_name,const char*,"const FelicaData*, NfcDeviceNameType"
|
Function,+,felica_get_device_name,const char*,"const FelicaData*, NfcDeviceNameType"
|
||||||
@@ -2215,6 +2220,13 @@ Function,+,iso15693_3_get_uid,const uint8_t*,"const Iso15693_3Data*, size_t*"
|
|||||||
Function,+,iso15693_3_is_block_locked,_Bool,"const Iso15693_3Data*, uint8_t"
|
Function,+,iso15693_3_is_block_locked,_Bool,"const Iso15693_3Data*, uint8_t"
|
||||||
Function,+,iso15693_3_is_equal,_Bool,"const Iso15693_3Data*, const Iso15693_3Data*"
|
Function,+,iso15693_3_is_equal,_Bool,"const Iso15693_3Data*, const Iso15693_3Data*"
|
||||||
Function,+,iso15693_3_load,_Bool,"Iso15693_3Data*, FlipperFormat*, uint32_t"
|
Function,+,iso15693_3_load,_Bool,"Iso15693_3Data*, FlipperFormat*, uint32_t"
|
||||||
|
Function,+,iso15693_3_poller_activate,Iso15693_3Error,"Iso15693_3Poller*, Iso15693_3Data*"
|
||||||
|
Function,+,iso15693_3_poller_get_blocks_security,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint16_t"
|
||||||
|
Function,+,iso15693_3_poller_get_system_info,Iso15693_3Error,"Iso15693_3Poller*, Iso15693_3SystemInfo*"
|
||||||
|
Function,+,iso15693_3_poller_inventory,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*"
|
||||||
|
Function,+,iso15693_3_poller_read_block,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint8_t, uint8_t"
|
||||||
|
Function,+,iso15693_3_poller_read_blocks,Iso15693_3Error,"Iso15693_3Poller*, uint8_t*, uint16_t, uint8_t"
|
||||||
|
Function,+,iso15693_3_poller_send_frame,Iso15693_3Error,"Iso15693_3Poller*, const BitBuffer*, BitBuffer*, uint32_t"
|
||||||
Function,+,iso15693_3_reset,void,Iso15693_3Data*
|
Function,+,iso15693_3_reset,void,Iso15693_3Data*
|
||||||
Function,+,iso15693_3_save,_Bool,"const Iso15693_3Data*, FlipperFormat*"
|
Function,+,iso15693_3_save,_Bool,"const Iso15693_3Data*, FlipperFormat*"
|
||||||
Function,+,iso15693_3_set_uid,_Bool,"Iso15693_3Data*, const uint8_t*, size_t"
|
Function,+,iso15693_3_set_uid,_Bool,"Iso15693_3Data*, const uint8_t*, size_t"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user