mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 12:51:22 +04:00
NFC FeliCa: Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks (#4254)
* SimpleArray attached to FelicaData
* tx rx done. response parsing done (in log)
* dynamic vector as buffer. rendering begin
* On screen render for directory tree
* flags in render to indicate is_public_readable
* beautify render flags
* format
* offload dynamic vector into individual files
* saving. exposed dir tree writing for double use
* save: additional formatting
* save: clean up and some additional notes
* load done
* delete unnecessary debug log
* Load: safer way to handle backward compatibility
`parsed` being true is only contingent on whether the header (device type, UID, etc) are correctly read. The detailed data can be absent if saved from previous versions.
Side effects:
1. The data format version number must not increment.
2. Newer sections of dumps must be appended in the end of the file.
* format
* handle block reading according to IC type
Old version was aimed for FeliCa Lite dumping, which doesn't apply to FeliCa standard. Thus they need to be diverged in the poller run workflow.
* read block content works. rendering begin
* Render Refactor: dir & dump view from submenu
* Render: show IC type name
* IC parsing function cleanup
* Revert "IC parsing function cleanup"
This reverts commit ee3f7bf125.
* Load: Standard dump. Fully backward compatible
* format
* sync API version
* format saved file
* delete unused variable
* clean ups
* IC type addition
* correction
* beautify attribute parsing
* correction
* Lite save: delete extra line
* correction: FeliCa link in Lite-S mode
* format
* Save: simplify printing
* update IC type parsing
* conform to api standard: const resp ptr to ptr
also slightly faster and more readable block dump loop
* disambiguate workflow type vs ic type
It was too confusing to have the ic name string telling you one thing and ic_type enum saying the other. Might as well use better naming to indicate the use case for the two things
* beautify on device render
* reject dynamic_vector, embrace m-array
* lint
* use full variable name
* partial fix: poller context's data proper init
* edit unit test dump IC code
and a small bug fix for the Lite auth workflow
* unit test felica dump PMm correction
* Fixes for static analysis warnings
---------
Co-authored-by: hedger <hedger@nanode.su>
Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@ UID: 29 9F FA 53 AB 75 87 6E
|
|||||||
# FeliCa specific data
|
# FeliCa specific data
|
||||||
Data format version: 1
|
Data format version: 1
|
||||||
Manufacture id: 29 9F FA 53 AB 75 87 6E
|
Manufacture id: 29 9F FA 53 AB 75 87 6E
|
||||||
Manufacture parameter: 57 4E 10 2A 94 16 BC 8E
|
Manufacture parameter: 00 F1 00 00 00 01 43 00
|
||||||
Blocks total: 28
|
Blocks total: 28
|
||||||
Blocks read: 28
|
Blocks read: 28
|
||||||
Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF
|
Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF
|
||||||
|
|||||||
@@ -39,17 +39,8 @@ static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent e
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
|
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
|
||||||
const NfcDevice* device = instance->nfc_device;
|
// Jump to advanced scene right away
|
||||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);
|
||||||
|
|
||||||
FuriString* temp_str = furi_string_alloc();
|
|
||||||
|
|
||||||
nfc_render_felica_dump(data, temp_str);
|
|
||||||
|
|
||||||
widget_add_text_scroll_element(
|
|
||||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
|
||||||
|
|
||||||
furi_string_free(temp_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {
|
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {
|
||||||
|
|||||||
@@ -4,9 +4,16 @@ void nfc_render_felica_blocks_count(
|
|||||||
const FelicaData* data,
|
const FelicaData* data,
|
||||||
FuriString* str,
|
FuriString* str,
|
||||||
bool render_auth_notification) {
|
bool render_auth_notification) {
|
||||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
if(data->workflow_type == FelicaLite) {
|
||||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
|
||||||
|
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||||
|
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||||
|
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||||
|
}
|
||||||
|
} else if(data->workflow_type == FelicaStandard) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +39,11 @@ void nfc_render_felica_info(
|
|||||||
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FuriString* ic_type_str = furi_string_alloc();
|
||||||
|
felica_get_ic_name(data, ic_type_str);
|
||||||
|
furi_string_cat_printf(str, "IC Type:\n%s\n", furi_string_get_cstr(ic_type_str));
|
||||||
|
furi_string_free(ic_type_str);
|
||||||
|
|
||||||
nfc_render_felica_idm(data, format_type, str);
|
nfc_render_felica_idm(data, format_type, str);
|
||||||
|
|
||||||
if(format_type == NfcProtocolFormatTypeFull) {
|
if(format_type == NfcProtocolFormatTypeFull) {
|
||||||
@@ -40,6 +52,14 @@ void nfc_render_felica_info(
|
|||||||
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
furi_string_cat_printf(str, "\n");
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"Services found: %lu \nAreas found: %lu\n",
|
||||||
|
simple_array_get_count(data->services),
|
||||||
|
simple_array_get_count(data->areas));
|
||||||
|
|
||||||
nfc_render_felica_blocks_count(data, str, true);
|
nfc_render_felica_blocks_count(data, str, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +79,18 @@ static void nfc_render_felica_block_name(
|
|||||||
|
|
||||||
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
||||||
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
||||||
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
|
||||||
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
|
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
|
||||||
furi_string_cat_printf(str, "%02X ", block->data[j]);
|
|
||||||
}
|
}
|
||||||
furi_string_cat_printf(str, "\n");
|
furi_string_cat_printf(str, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {
|
||||||
|
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
|
||||||
|
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void nfc_render_felica_block(
|
static void nfc_render_felica_block(
|
||||||
const FelicaBlock* block,
|
const FelicaBlock* block,
|
||||||
FuriString* str,
|
FuriString* str,
|
||||||
@@ -76,8 +101,13 @@ static void nfc_render_felica_block(
|
|||||||
nfc_render_felica_block_data(block, str);
|
nfc_render_felica_block_data(block, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {
|
||||||
FuriString* name = furi_string_alloc();
|
FuriString* name = furi_string_alloc();
|
||||||
|
|
||||||
|
furi_string_cat_printf(str, "\e#Blocks read:\n");
|
||||||
|
|
||||||
|
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||||
|
|
||||||
for(size_t i = 0; i < 14; i++) {
|
for(size_t i = 0; i < 14; i++) {
|
||||||
furi_string_printf(name, "S_PAD%d", i);
|
furi_string_printf(name, "S_PAD%d", i);
|
||||||
uint8_t suf_cnt = 18;
|
uint8_t suf_cnt = 18;
|
||||||
@@ -105,3 +135,70 @@ void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
|||||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
|
||||||
|
const size_t area_count = simple_array_get_count(data->areas);
|
||||||
|
const size_t service_count = simple_array_get_count(data->services);
|
||||||
|
|
||||||
|
furi_string_cat_printf(str, "\e#Directory Tree:\n");
|
||||||
|
|
||||||
|
if(area_count == 0 || service_count == 0) {
|
||||||
|
furi_string_cat_printf(str, "No services or areas found.\n");
|
||||||
|
} else {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str, "%zu areas found.\n%zu services found.\n\n", area_count, service_count);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str, "::: ... are readable services\n||| ... are locked services\n");
|
||||||
|
}
|
||||||
|
felica_write_directory_tree(data, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_more_info_render_felica_blocks(
|
||||||
|
const FelicaData* data,
|
||||||
|
FuriString* str,
|
||||||
|
const uint16_t service_code_key) {
|
||||||
|
furi_string_cat_printf(str, "\n");
|
||||||
|
if(data->workflow_type == FelicaLite) {
|
||||||
|
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||||
|
FuriString* name = furi_string_alloc();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < 14; i++) {
|
||||||
|
furi_string_printf(name, "S_PAD%d", i);
|
||||||
|
uint8_t suf_cnt = 18;
|
||||||
|
if(i == 1) {
|
||||||
|
suf_cnt = 19;
|
||||||
|
} else if((i == 10) || (i == 12) || (i == 13)) {
|
||||||
|
suf_cnt = 16;
|
||||||
|
}
|
||||||
|
nfc_render_felica_block(
|
||||||
|
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
|
||||||
|
}
|
||||||
|
furi_string_free(name);
|
||||||
|
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
|
||||||
|
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
|
||||||
|
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
|
||||||
|
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
|
||||||
|
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
|
||||||
|
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
|
||||||
|
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
|
||||||
|
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
|
||||||
|
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
|
||||||
|
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
|
||||||
|
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
|
||||||
|
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
|
||||||
|
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||||
|
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||||
|
|
||||||
|
} else if(data->workflow_type == FelicaStandard) {
|
||||||
|
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
|
||||||
|
for(size_t i = 0; i < public_blocks_count; i++) {
|
||||||
|
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
||||||
|
if(public_block->service_code != service_code_key) {
|
||||||
|
continue; // Skip blocks not matching the requested service code
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(str, "-----Block 0x%02X-----\n", public_block->block_idx);
|
||||||
|
nfc_render_felica_block_data_simple(&public_block->block, str);
|
||||||
|
furi_string_cat_printf(str, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,9 +14,16 @@ void nfc_render_felica_info(
|
|||||||
NfcProtocolFormatType format_type,
|
NfcProtocolFormatType format_type,
|
||||||
FuriString* str);
|
FuriString* str);
|
||||||
|
|
||||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
|
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);
|
||||||
|
|
||||||
void nfc_render_felica_idm(
|
void nfc_render_felica_idm(
|
||||||
const FelicaData* data,
|
const FelicaData* data,
|
||||||
NfcProtocolFormatType format_type,
|
NfcProtocolFormatType format_type,
|
||||||
FuriString* str);
|
FuriString* str);
|
||||||
|
|
||||||
|
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);
|
||||||
|
|
||||||
|
void nfc_more_info_render_felica_blocks(
|
||||||
|
const FelicaData* data,
|
||||||
|
FuriString* str,
|
||||||
|
const uint16_t service_code_key);
|
||||||
|
|||||||
@@ -74,4 +74,6 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput)
|
|||||||
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
|
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
|
||||||
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
|
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
|
||||||
|
|
||||||
|
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
|
||||||
|
|
||||||
ADD_SCENE(nfc, generate_info, GenerateInfo)
|
ADD_SCENE(nfc, generate_info, GenerateInfo)
|
||||||
|
|||||||
151
applications/main/nfc/scenes/nfc_scene_felica_more_info.c
Normal file
151
applications/main/nfc/scenes/nfc_scene_felica_more_info.c
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
|
||||||
|
#include "../helpers/protocol_support/felica/felica_render.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FelicaMoreInfoStateMenu,
|
||||||
|
FelicaMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SubmenuIndex {
|
||||||
|
SubmenuIndexDirectory,
|
||||||
|
SubmenuIndexDynamic, // dynamic indices start here
|
||||||
|
};
|
||||||
|
|
||||||
|
void nfc_scene_felica_more_info_on_enter(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
Submenu* submenu = nfc->submenu;
|
||||||
|
|
||||||
|
const uint32_t state =
|
||||||
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
||||||
|
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Directory",
|
||||||
|
SubmenuIndexDirectory,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
|
||||||
|
FuriString* label = furi_string_alloc();
|
||||||
|
|
||||||
|
switch(data->workflow_type) {
|
||||||
|
case FelicaLite:
|
||||||
|
furi_string_printf(label, "All blocks");
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
furi_string_get_cstr(label),
|
||||||
|
SubmenuIndexDynamic,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
break;
|
||||||
|
case FelicaStandard:
|
||||||
|
for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) {
|
||||||
|
const FelicaService* service = simple_array_cget(data->services, i);
|
||||||
|
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
|
||||||
|
if(!is_public) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
furi_string_printf(label, "Readable serv %04X", service->code);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
furi_string_get_cstr(label),
|
||||||
|
i + SubmenuIndexDynamic,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(label);
|
||||||
|
|
||||||
|
if(state >= FelicaMoreInfoStateItem) {
|
||||||
|
submenu_set_selected_item(
|
||||||
|
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
const uint32_t state =
|
||||||
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
|
||||||
|
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == SubmenuIndexDirectory) {
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
nfc_more_info_render_felica_dir(data, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager,
|
||||||
|
NfcSceneFelicaMoreInfo,
|
||||||
|
FelicaMoreInfoStateItem + SubmenuIndexDirectory);
|
||||||
|
consumed = true;
|
||||||
|
} else {
|
||||||
|
const uint16_t service_ind = event.event - 1; // offset the three enums above
|
||||||
|
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
furi_string_reset(nfc->text_box_store);
|
||||||
|
|
||||||
|
switch(data->workflow_type) {
|
||||||
|
case FelicaLite:
|
||||||
|
nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store);
|
||||||
|
break;
|
||||||
|
case FelicaStandard:
|
||||||
|
const FelicaService* service = simple_array_cget(data->services, service_ind);
|
||||||
|
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
|
||||||
|
nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_string_set_str(nfc->text_box_store, "IC type not implemented yet");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
text_box_set_font(nfc->text_box, TextBoxFontHex);
|
||||||
|
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
if(state >= FelicaMoreInfoStateItem) {
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
|
||||||
|
} else {
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
// Return directly to the Info scene
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_felica_more_info_on_exit(void* context) {
|
||||||
|
NfcApp* nfc = context;
|
||||||
|
|
||||||
|
// Clear views
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
furi_string_reset(nfc->text_box_store);
|
||||||
|
submenu_reset(nfc->submenu);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "felica.h"
|
#include "felica_i.h"
|
||||||
|
#include <lib/toolbox/hex.h>
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
#define FELICA_MANUFACTURE_ID "Manufacture id"
|
#define FELICA_MANUFACTURE_ID "Manufacture id"
|
||||||
#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter"
|
#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter"
|
||||||
|
|
||||||
static const uint32_t felica_data_format_version = 1;
|
static const uint32_t felica_data_format_version = 2;
|
||||||
|
|
||||||
/** @brief This is used in felica_prepare_first_block to define which
|
/** @brief This is used in felica_prepare_first_block to define which
|
||||||
* type of block needs to be prepared.
|
* type of block needs to be prepared.
|
||||||
@@ -39,24 +40,71 @@ const NfcDeviceBase nfc_device_felica = {
|
|||||||
|
|
||||||
FelicaData* felica_alloc(void) {
|
FelicaData* felica_alloc(void) {
|
||||||
FelicaData* data = malloc(sizeof(FelicaData));
|
FelicaData* data = malloc(sizeof(FelicaData));
|
||||||
|
furi_check(data);
|
||||||
|
|
||||||
|
data->services = simple_array_alloc(&felica_service_array_cfg);
|
||||||
|
data->areas = simple_array_alloc(&felica_area_array_cfg);
|
||||||
|
data->public_blocks = simple_array_alloc(&felica_public_block_array_cfg);
|
||||||
|
furi_check(data->services);
|
||||||
|
furi_check(data->areas);
|
||||||
|
furi_check(data->public_blocks);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_free(FelicaData* data) {
|
void felica_free(FelicaData* data) {
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
|
furi_check(data->services);
|
||||||
|
simple_array_free(data->services);
|
||||||
|
|
||||||
|
furi_check(data->areas);
|
||||||
|
simple_array_free(data->areas);
|
||||||
|
|
||||||
|
furi_check(data->public_blocks);
|
||||||
|
simple_array_free(data->public_blocks);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_reset(FelicaData* data) {
|
void felica_reset(FelicaData* data) {
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
memset(data, 0, sizeof(FelicaData));
|
|
||||||
|
if(data->services) {
|
||||||
|
simple_array_reset(data->services);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data->areas) {
|
||||||
|
simple_array_reset(data->areas);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data->public_blocks) {
|
||||||
|
simple_array_reset(data->public_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
data->blocks_read = 0;
|
||||||
|
data->blocks_total = 0;
|
||||||
|
data->workflow_type = FelicaUnknown;
|
||||||
|
memset(&data->idm, 0, sizeof(data->idm));
|
||||||
|
memset(&data->pmm, 0, sizeof(data->pmm));
|
||||||
|
memset(&data->data, 0, sizeof(data->data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void felica_copy(FelicaData* data, const FelicaData* other) {
|
void felica_copy(FelicaData* data, const FelicaData* other) {
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
furi_check(other);
|
furi_check(other);
|
||||||
|
|
||||||
*data = *other;
|
felica_reset(data);
|
||||||
|
|
||||||
|
data->idm = other->idm;
|
||||||
|
data->pmm = other->pmm;
|
||||||
|
data->blocks_total = other->blocks_total;
|
||||||
|
data->blocks_read = other->blocks_read;
|
||||||
|
data->data = other->data;
|
||||||
|
data->workflow_type = other->workflow_type;
|
||||||
|
|
||||||
|
simple_array_copy(data->services, other->services);
|
||||||
|
simple_array_copy(data->areas, other->areas);
|
||||||
|
simple_array_copy(data->public_blocks, other->public_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool felica_verify(FelicaData* data, const FuriString* device_type) {
|
bool felica_verify(FelicaData* data, const FuriString* device_type) {
|
||||||
@@ -70,43 +118,164 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
|||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
FuriString* str_key_buffer = furi_string_alloc();
|
||||||
|
FuriString* str_data_buffer = furi_string_alloc();
|
||||||
|
|
||||||
|
// Header
|
||||||
do {
|
do {
|
||||||
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
|
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
|
||||||
|
|
||||||
uint32_t data_format_version = 0;
|
uint32_t data_format_version = 0;
|
||||||
if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))
|
if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))
|
||||||
break;
|
break;
|
||||||
if(data_format_version != felica_data_format_version) break;
|
|
||||||
|
// V1 saving function always treated everything as Felica Lite
|
||||||
|
// So we load the blocks as if everything is Felica Lite
|
||||||
|
|
||||||
if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
|
if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
|
||||||
break;
|
break;
|
||||||
if(!flipper_format_read_hex(
|
if(!flipper_format_read_hex(
|
||||||
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
parsed = true;
|
felica_get_workflow_type(data);
|
||||||
uint32_t blocks_total = 0;
|
if(data_format_version == 1) {
|
||||||
uint32_t blocks_read = 0;
|
data->workflow_type = FelicaLite;
|
||||||
if(!flipper_format_read_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
|
||||||
if(!flipper_format_read_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
|
||||||
data->blocks_total = (uint8_t)blocks_total;
|
|
||||||
data->blocks_read = (uint8_t)blocks_read;
|
|
||||||
|
|
||||||
FuriString* temp_str = furi_string_alloc();
|
|
||||||
for(uint8_t i = 0; i < data->blocks_total; i++) {
|
|
||||||
furi_string_printf(temp_str, "Block %d", i);
|
|
||||||
if(!flipper_format_read_hex(
|
|
||||||
ff,
|
|
||||||
furi_string_get_cstr(temp_str),
|
|
||||||
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
|
||||||
sizeof(FelicaBlock))) {
|
|
||||||
parsed = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
furi_string_free(temp_str);
|
parsed = true;
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
|
if(!parsed) {
|
||||||
|
furi_string_free(str_key_buffer);
|
||||||
|
furi_string_free(str_data_buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(data->workflow_type) {
|
||||||
|
case FelicaLite:
|
||||||
|
// Blocks data
|
||||||
|
do {
|
||||||
|
uint32_t blocks_total = 0;
|
||||||
|
uint32_t blocks_read = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
||||||
|
data->blocks_total = (uint8_t)blocks_total;
|
||||||
|
data->blocks_read = (uint8_t)blocks_read;
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < data->blocks_total; i++) {
|
||||||
|
furi_string_printf(str_data_buffer, "Block %d", i);
|
||||||
|
if(!flipper_format_read_hex(
|
||||||
|
ff,
|
||||||
|
furi_string_get_cstr(str_data_buffer),
|
||||||
|
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||||
|
sizeof(FelicaBlock))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
break;
|
||||||
|
case FelicaStandard:
|
||||||
|
// Areas
|
||||||
|
do {
|
||||||
|
uint32_t area_count = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Area found", &area_count, 1)) break;
|
||||||
|
simple_array_init(data->areas, area_count);
|
||||||
|
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
for(uint16_t i = 0; i < area_count; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Area %03X", i);
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FelicaArea* area = simple_array_get(data->areas, i);
|
||||||
|
if(sscanf(
|
||||||
|
furi_string_get_cstr(str_data_buffer),
|
||||||
|
"| Code %04hX | Services #%03hX-#%03hX |",
|
||||||
|
&area->code,
|
||||||
|
&area->first_idx,
|
||||||
|
&area->last_idx) != 3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
// Services
|
||||||
|
do {
|
||||||
|
uint32_t service_count = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Service found", &service_count, 1)) break;
|
||||||
|
simple_array_init(data->services, service_count);
|
||||||
|
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
for(uint16_t i = 0; i < service_count; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Service %03X", i);
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FelicaService* service = simple_array_get(data->services, i);
|
||||||
|
|
||||||
|
// all unread in the beginning. reserved for future block load
|
||||||
|
if(!sscanf(
|
||||||
|
furi_string_get_cstr(str_data_buffer), "| Code %04hX |", &service->code)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
service->attr = service->code & 0x3F;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
// Public blocks
|
||||||
|
do {
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
uint32_t public_block_count = 0;
|
||||||
|
if(!flipper_format_read_uint32(ff, "Public blocks read", &public_block_count, 1))
|
||||||
|
break;
|
||||||
|
simple_array_init(data->public_blocks, public_block_count);
|
||||||
|
for(uint16_t i = 0; i < public_block_count; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Block %04X", i);
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
||||||
|
if(sscanf(
|
||||||
|
furi_string_get_cstr(str_data_buffer),
|
||||||
|
"| Service code %04hX | Block index %02hhX |",
|
||||||
|
&public_block->service_code,
|
||||||
|
&public_block->block_idx) != 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t needle = furi_string_search_str(str_data_buffer, "Data: ");
|
||||||
|
if(needle == FURI_STRING_FAILURE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
needle += 6; // length of "Data: " = 6
|
||||||
|
furi_string_mid(str_data_buffer, needle, 3 * FELICA_DATA_BLOCK_SIZE);
|
||||||
|
furi_string_replace_all(str_data_buffer, " ", "");
|
||||||
|
if(!hex_chars_to_uint8(
|
||||||
|
furi_string_get_cstr(str_data_buffer), public_block->block.data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||||
|
furi_string_cat_printf(str_data_buffer, "%02X ", public_block->block.data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(str_key_buffer);
|
||||||
|
furi_string_free(str_data_buffer);
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +283,10 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
|||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
bool saved = false;
|
bool saved = false;
|
||||||
|
FuriString* str_data_buffer = furi_string_alloc();
|
||||||
|
FuriString* str_key_buffer = furi_string_alloc();
|
||||||
do {
|
do {
|
||||||
|
// Header
|
||||||
if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break;
|
if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break;
|
||||||
if(!flipper_format_write_uint32(
|
if(!flipper_format_write_uint32(
|
||||||
ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1))
|
ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1))
|
||||||
@@ -126,27 +297,134 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
|||||||
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
uint32_t blocks_total = data->blocks_total;
|
|
||||||
uint32_t blocks_read = data->blocks_read;
|
|
||||||
if(!flipper_format_write_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
|
||||||
if(!flipper_format_write_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
|
||||||
|
|
||||||
saved = true;
|
saved = true;
|
||||||
FuriString* temp_str = furi_string_alloc();
|
|
||||||
for(uint8_t i = 0; i < blocks_total; i++) {
|
felica_get_ic_name(data, str_data_buffer);
|
||||||
furi_string_printf(temp_str, "Block %d", i);
|
furi_string_replace_all(str_data_buffer, "\n", " ");
|
||||||
if(!flipper_format_write_hex(
|
if(!flipper_format_write_string(ff, "IC Type", str_data_buffer)) break;
|
||||||
ff,
|
if(!flipper_format_write_empty_line(ff)) break;
|
||||||
furi_string_get_cstr(temp_str),
|
|
||||||
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
|
||||||
sizeof(FelicaBlock))) {
|
|
||||||
saved = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
furi_string_free(temp_str);
|
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
|
switch(data->workflow_type) {
|
||||||
|
case FelicaLite:
|
||||||
|
if(!flipper_format_write_comment_cstr(ff, "Felica Lite specific data")) break;
|
||||||
|
// Blocks count
|
||||||
|
do {
|
||||||
|
uint32_t blocks_total = data->blocks_total;
|
||||||
|
uint32_t blocks_read = data->blocks_read;
|
||||||
|
if(!flipper_format_write_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
||||||
|
if(!flipper_format_write_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
||||||
|
|
||||||
|
// Blocks data
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint8_t i = 0; i < blocks_total; i++) {
|
||||||
|
furi_string_printf(str_key_buffer, "Block %d", i);
|
||||||
|
if(!flipper_format_write_hex(
|
||||||
|
ff,
|
||||||
|
furi_string_get_cstr(str_key_buffer),
|
||||||
|
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||||
|
sizeof(FelicaBlock))) {
|
||||||
|
saved = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FelicaStandard:
|
||||||
|
if(!flipper_format_write_comment_cstr(ff, "Felica Standard specific data")) break;
|
||||||
|
|
||||||
|
do {
|
||||||
|
uint32_t area_count = simple_array_get_count(data->areas);
|
||||||
|
uint32_t service_count = simple_array_get_count(data->services);
|
||||||
|
// Note: The theoretical max area/service count is 2^10
|
||||||
|
// So uint16_t is already enough for practical usage
|
||||||
|
// The following key index print will use %03X because 12 bits are enough to cover 0-1023
|
||||||
|
|
||||||
|
// Area count
|
||||||
|
if(!flipper_format_write_uint32(ff, "Area found", &area_count, 1)) break;
|
||||||
|
|
||||||
|
// Area data
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint16_t i = 0; i < area_count; i++) {
|
||||||
|
FelicaArea* area = simple_array_get(data->areas, i);
|
||||||
|
furi_string_printf(str_key_buffer, "Area %03X", i);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer,
|
||||||
|
"| Code %04X | Services #%03X-#%03X |",
|
||||||
|
area->code,
|
||||||
|
area->first_idx,
|
||||||
|
area->last_idx);
|
||||||
|
if(!flipper_format_write_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_write_empty_line(ff)) break;
|
||||||
|
|
||||||
|
// Service count
|
||||||
|
if(!flipper_format_write_uint32(ff, "Service found", &service_count, 1)) break;
|
||||||
|
|
||||||
|
// Service data
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint16_t i = 0; i < service_count; i++) {
|
||||||
|
FelicaService* service = simple_array_get(data->services, i);
|
||||||
|
furi_string_printf(str_key_buffer, "Service %03X", i);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer, "| Code %04X | Attrib. %02X ", service->code, service->attr);
|
||||||
|
felica_service_get_attribute_string(service, str_data_buffer);
|
||||||
|
if(!flipper_format_write_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_write_empty_line(ff)) break;
|
||||||
|
|
||||||
|
// Directory tree
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer, "\n::: ... are public services\n||| ... are private services");
|
||||||
|
felica_write_directory_tree(data, str_data_buffer);
|
||||||
|
furi_string_replace_all(str_data_buffer, ":", "+");
|
||||||
|
// We use a clearer marker in saved text files
|
||||||
|
if(!flipper_format_write_string(ff, "Directory Tree", str_data_buffer)) break;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
// Public blocks
|
||||||
|
do {
|
||||||
|
uint32_t public_block_count = simple_array_get_count(data->public_blocks);
|
||||||
|
if(!flipper_format_write_uint32(ff, "Public blocks read", &public_block_count, 1))
|
||||||
|
break;
|
||||||
|
furi_string_reset(str_data_buffer);
|
||||||
|
furi_string_reset(str_key_buffer);
|
||||||
|
for(uint16_t i = 0; i < public_block_count; i++) {
|
||||||
|
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
||||||
|
furi_string_printf(str_key_buffer, "Block %04X", i);
|
||||||
|
furi_string_printf(
|
||||||
|
str_data_buffer,
|
||||||
|
"| Service code %04X | Block index %02X | Data: ",
|
||||||
|
public_block->service_code,
|
||||||
|
public_block->block_idx);
|
||||||
|
for(uint8_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||||
|
furi_string_cat_printf(str_data_buffer, "%02X ", public_block->block.data[j]);
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(str_data_buffer, "|");
|
||||||
|
if(!flipper_format_write_string(
|
||||||
|
ff, furi_string_get_cstr(str_key_buffer), str_data_buffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
furi_string_free(str_data_buffer);
|
||||||
|
furi_string_free(str_key_buffer);
|
||||||
|
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +432,13 @@ bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
|
|||||||
furi_check(data);
|
furi_check(data);
|
||||||
furi_check(other);
|
furi_check(other);
|
||||||
|
|
||||||
return memcmp(data, other, sizeof(FelicaData)) == 0;
|
return memcmp(data->idm.data, other->idm.data, sizeof(FelicaIDm)) == 0 &&
|
||||||
|
memcmp(data->pmm.data, other->pmm.data, sizeof(FelicaPMm)) == 0 &&
|
||||||
|
data->blocks_total == other->blocks_total && data->blocks_read == other->blocks_read &&
|
||||||
|
memcmp(&data->data, &other->data, sizeof(data->data)) == 0 &&
|
||||||
|
simple_array_is_equal(data->services, other->services) &&
|
||||||
|
simple_array_is_equal(data->areas, other->areas) &&
|
||||||
|
simple_array_is_equal(data->public_blocks, other->public_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
|
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
|
||||||
@@ -355,3 +639,251 @@ void felica_calculate_mac_write(
|
|||||||
memcpy(session_swapped + 8, session_key, 8);
|
memcpy(session_swapped + 8, session_key, 8);
|
||||||
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
|
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void felica_write_directory_tree(const FelicaData* data, FuriString* str) {
|
||||||
|
furi_check(data);
|
||||||
|
furi_check(str);
|
||||||
|
|
||||||
|
furi_string_cat_str(str, "\n");
|
||||||
|
|
||||||
|
uint16_t area_last_stack[8];
|
||||||
|
uint8_t depth = 0;
|
||||||
|
|
||||||
|
size_t area_iter = 0;
|
||||||
|
const size_t area_count = simple_array_get_count(data->areas);
|
||||||
|
const size_t service_count = simple_array_get_count(data->services);
|
||||||
|
|
||||||
|
for(size_t svc_idx = 0; svc_idx < service_count; ++svc_idx) {
|
||||||
|
while(area_iter < area_count) {
|
||||||
|
const FelicaArea* next_area = simple_array_get(data->areas, area_iter);
|
||||||
|
if(next_area->first_idx != svc_idx) break;
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < depth - 1; ++i)
|
||||||
|
furi_string_cat_printf(str, "| ");
|
||||||
|
furi_string_cat_printf(str, depth ? "|" : "");
|
||||||
|
furi_string_cat_printf(str, "- AREA_%04X/\n", next_area->code >> 6);
|
||||||
|
|
||||||
|
area_last_stack[depth++] = next_area->last_idx;
|
||||||
|
area_iter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FelicaService* service = simple_array_get(data->services, svc_idx);
|
||||||
|
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0;
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < depth - 1; ++i)
|
||||||
|
furi_string_cat_printf(str, is_public ? ": " : "| ");
|
||||||
|
furi_string_cat_printf(str, is_public ? ":" : "|");
|
||||||
|
furi_string_cat_printf(str, "- serv_%04X\n", service->code);
|
||||||
|
|
||||||
|
if(depth && svc_idx >= area_last_stack[depth - 1]) depth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_get_workflow_type(FelicaData* data) {
|
||||||
|
// Reference: Proxmark3 repo
|
||||||
|
uint8_t rom_type = data->pmm.data[0];
|
||||||
|
uint8_t workflow_type = data->pmm.data[1];
|
||||||
|
if(workflow_type <= 0x48) {
|
||||||
|
// More liberal check because most of these should be treated as FeliCa Standard, regardless of mobile or not.
|
||||||
|
data->workflow_type = FelicaStandard;
|
||||||
|
} else {
|
||||||
|
switch(workflow_type) {
|
||||||
|
case 0xA2:
|
||||||
|
data->workflow_type = FelicaStandard;
|
||||||
|
break;
|
||||||
|
case 0xF0:
|
||||||
|
case 0xF1:
|
||||||
|
case 0xF2: // 0xF2 => FeliCa Link RC-S967 in Lite-S Mode or Lite-S HT Mode
|
||||||
|
data->workflow_type = FelicaLite;
|
||||||
|
break;
|
||||||
|
case 0xE1: // Felica Link
|
||||||
|
case 0xE0: // Felica Plug
|
||||||
|
data->workflow_type = FelicaUnknown;
|
||||||
|
break;
|
||||||
|
case 0xFF:
|
||||||
|
if(rom_type == 0xFF) {
|
||||||
|
data->workflow_type = FelicaUnknown; // Felica Link
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data->workflow_type = FelicaUnknown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_get_ic_name(const FelicaData* data, FuriString* ic_name) {
|
||||||
|
// Reference: Proxmark3 repo
|
||||||
|
uint8_t rom_type = data->pmm.data[0];
|
||||||
|
uint8_t ic_type = data->pmm.data[1];
|
||||||
|
|
||||||
|
switch(ic_type) {
|
||||||
|
// FeliCa Standard Products:
|
||||||
|
// odd findings
|
||||||
|
case 0x00:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S830");
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S915");
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S919");
|
||||||
|
break;
|
||||||
|
case 0x06:
|
||||||
|
case 0x07:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Mobile IC,\nChip V1.0");
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S952");
|
||||||
|
break;
|
||||||
|
case 0x09:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S953");
|
||||||
|
break;
|
||||||
|
case 0x0B:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S9X4,\nJapan Transit IC");
|
||||||
|
break;
|
||||||
|
case 0x0C:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S954");
|
||||||
|
break;
|
||||||
|
case 0x0D:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S960");
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
case 0x11:
|
||||||
|
case 0x12:
|
||||||
|
case 0x13:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Mobile IC,\nChip V2.0");
|
||||||
|
break;
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Mobile IC,\nChip V3.0");
|
||||||
|
break;
|
||||||
|
case 0x16:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Mobile IC,\nJapan Transit IC");
|
||||||
|
break;
|
||||||
|
case 0x17:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Mobile IC,\nChip V4.0");
|
||||||
|
break;
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
|
case 0x1B:
|
||||||
|
case 0x1C:
|
||||||
|
case 0x1D:
|
||||||
|
case 0x1E:
|
||||||
|
case 0x1F:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Mobile IC,\nChip V4.1");
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S962");
|
||||||
|
// RC-S962 has been extensively found in Japan Transit ICs, despite model number not ending in 4
|
||||||
|
break;
|
||||||
|
case 0x31:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-S104,\nJapan Transit IC");
|
||||||
|
break;
|
||||||
|
case 0x32:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA00/1");
|
||||||
|
break;
|
||||||
|
case 0x33:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA00/2");
|
||||||
|
break;
|
||||||
|
case 0x34:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA01/1");
|
||||||
|
break;
|
||||||
|
case 0x35:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA01/2");
|
||||||
|
break;
|
||||||
|
case 0x36:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA04/1,\nJapan Transit IC");
|
||||||
|
break;
|
||||||
|
case 0x3E:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA08/1");
|
||||||
|
break;
|
||||||
|
case 0x43:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA24/1");
|
||||||
|
break;
|
||||||
|
case 0x44:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA20/1");
|
||||||
|
break;
|
||||||
|
case 0x45:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA20/2");
|
||||||
|
break;
|
||||||
|
case 0x46:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA21/2");
|
||||||
|
break;
|
||||||
|
case 0x47:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA24/1x1");
|
||||||
|
break;
|
||||||
|
case 0x48:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA21/2x1");
|
||||||
|
break;
|
||||||
|
case 0xA2:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Standard RC-SA14");
|
||||||
|
break;
|
||||||
|
// NFC Dynamic Tag (FeliCa Plug) Products:
|
||||||
|
case 0xE0:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Plug RC-S926,\nNFC Dynamic Tag");
|
||||||
|
break;
|
||||||
|
case 0xE1:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Link RC-S967,\nPlug Mode");
|
||||||
|
break;
|
||||||
|
case 0xF0:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Lite RC-S965");
|
||||||
|
break;
|
||||||
|
case 0xF1:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Lite-S RC-S966");
|
||||||
|
break;
|
||||||
|
case 0xF2:
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Link RC-S967,\nLite-S Mode or Lite-S HT Mode");
|
||||||
|
break;
|
||||||
|
case 0xFF:
|
||||||
|
if(rom_type == 0xFF) { // from FeliCa Link User's Manual
|
||||||
|
furi_string_set_str(ic_name, "FeliCa Link RC-S967,\nNFC-DEP Mode");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_string_printf(
|
||||||
|
ic_name,
|
||||||
|
"Unknown IC %02X ROM %02X:\nPlease submit an issue on\nGitHub and help us identify.",
|
||||||
|
ic_type,
|
||||||
|
rom_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void felica_service_get_attribute_string(const FelicaService* service, FuriString* str) {
|
||||||
|
furi_check(service);
|
||||||
|
furi_check(str);
|
||||||
|
|
||||||
|
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0;
|
||||||
|
furi_string_cat_str(str, is_public ? "| Public " : "| Private ");
|
||||||
|
|
||||||
|
bool is_purse = (service->attr & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0;
|
||||||
|
// Subfield bitwise attributes are applicable depending on is PURSE or not
|
||||||
|
|
||||||
|
if(is_purse) {
|
||||||
|
furi_string_cat_str(str, "| Purse |");
|
||||||
|
switch((service->attr & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) {
|
||||||
|
case 0:
|
||||||
|
furi_string_cat_str(str, " Direct |");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
furi_string_cat_str(str, " Cashback |");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
furi_string_cat_str(str, " Decrement |");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
furi_string_cat_str(str, " Read Only |");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
furi_string_cat_str(str, " Unknown |");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool is_random = (service->attr & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0;
|
||||||
|
furi_string_cat_str(str, is_random ? "| Random |" : "| Cyclic |");
|
||||||
|
bool is_readonly = (service->attr & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0;
|
||||||
|
furi_string_cat_str(str, is_readonly ? " Read Only |" : " Read/Write |");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <toolbox/bit_buffer.h>
|
#include <toolbox/bit_buffer.h>
|
||||||
#include <nfc/protocols/nfc_device_base_i.h>
|
#include <nfc/protocols/nfc_device_base_i.h>
|
||||||
#include <mbedtls/include/mbedtls/des.h>
|
#include <mbedtls/include/mbedtls/des.h>
|
||||||
|
#include <lib/toolbox/simple_array.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -34,6 +35,8 @@ extern "C" {
|
|||||||
#define FELICA_BLOCK_INDEX_STATE (0x92U)
|
#define FELICA_BLOCK_INDEX_STATE (0x92U)
|
||||||
#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)
|
#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)
|
||||||
|
|
||||||
|
#define FELICA_STANDARD_MAX_BLOCK_COUNT (0xFFU)
|
||||||
|
|
||||||
#define FELICA_GUARD_TIME_US (20000U)
|
#define FELICA_GUARD_TIME_US (20000U)
|
||||||
#define FELICA_FDT_POLL_FC (10000U)
|
#define FELICA_FDT_POLL_FC (10000U)
|
||||||
#define FELICA_POLL_POLL_MIN_US (1280U)
|
#define FELICA_POLL_POLL_MIN_US (1280U)
|
||||||
@@ -47,6 +50,16 @@ extern "C" {
|
|||||||
#define FELICA_TIME_SLOT_8 (0x07U)
|
#define FELICA_TIME_SLOT_8 (0x07U)
|
||||||
#define FELICA_TIME_SLOT_16 (0x0FU)
|
#define FELICA_TIME_SLOT_16 (0x0FU)
|
||||||
|
|
||||||
|
#define FELICA_CMD_LIST_SERVICE_CODE 0x0A
|
||||||
|
#define FELICA_CMD_LIST_SERVICE_CODE_RESP 0x0B
|
||||||
|
|
||||||
|
#define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001)
|
||||||
|
#define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010)
|
||||||
|
#define FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS (0b001000)
|
||||||
|
#define FELICA_SERVICE_ATTRIBUTE_CYCLIC (0b001100)
|
||||||
|
#define FELICA_SERVICE_ATTRIBUTE_PURSE (0b010000)
|
||||||
|
#define FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD (0b000110)
|
||||||
|
|
||||||
/** @brief Type of possible Felica errors */
|
/** @brief Type of possible Felica errors */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FelicaErrorNone,
|
FelicaErrorNone,
|
||||||
@@ -61,6 +74,12 @@ typedef enum {
|
|||||||
FelicaErrorFeatureUnsupported,
|
FelicaErrorFeatureUnsupported,
|
||||||
} FelicaError;
|
} FelicaError;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FelicaUnknown,
|
||||||
|
FelicaStandard,
|
||||||
|
FelicaLite,
|
||||||
|
} FelicaWorkflowType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t data[FELICA_DATA_BLOCK_SIZE];
|
uint8_t data[FELICA_DATA_BLOCK_SIZE];
|
||||||
} FelicaBlockData;
|
} FelicaBlockData;
|
||||||
@@ -146,6 +165,23 @@ typedef union {
|
|||||||
uint8_t dump[sizeof(FelicaFileSystem)];
|
uint8_t dump[sizeof(FelicaFileSystem)];
|
||||||
} FelicaFSUnion;
|
} FelicaFSUnion;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t code;
|
||||||
|
uint8_t attr;
|
||||||
|
} FelicaService;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t code;
|
||||||
|
uint16_t first_idx;
|
||||||
|
uint16_t last_idx;
|
||||||
|
} FelicaArea;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FelicaBlock block;
|
||||||
|
uint16_t service_code;
|
||||||
|
uint8_t block_idx;
|
||||||
|
} FelicaPublicBlock;
|
||||||
|
|
||||||
/** @brief Structure used to store Felica data and additional values about reading */
|
/** @brief Structure used to store Felica data and additional values about reading */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FelicaIDm idm;
|
FelicaIDm idm;
|
||||||
@@ -153,6 +189,11 @@ typedef struct {
|
|||||||
uint8_t blocks_total;
|
uint8_t blocks_total;
|
||||||
uint8_t blocks_read;
|
uint8_t blocks_read;
|
||||||
FelicaFSUnion data;
|
FelicaFSUnion data;
|
||||||
|
|
||||||
|
SimpleArray* services;
|
||||||
|
SimpleArray* areas;
|
||||||
|
SimpleArray* public_blocks;
|
||||||
|
FelicaWorkflowType workflow_type;
|
||||||
} FelicaData;
|
} FelicaData;
|
||||||
|
|
||||||
typedef struct FURI_PACKED {
|
typedef struct FURI_PACKED {
|
||||||
@@ -171,6 +212,14 @@ typedef struct {
|
|||||||
uint8_t SF2;
|
uint8_t SF2;
|
||||||
} FelicaCommandResponseHeader;
|
} FelicaCommandResponseHeader;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t command;
|
||||||
|
FelicaIDm idm;
|
||||||
|
} FelicaCommandHeaderRaw;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t service_code : 4;
|
uint8_t service_code : 4;
|
||||||
uint8_t access_mode : 3;
|
uint8_t access_mode : 3;
|
||||||
@@ -194,6 +243,11 @@ typedef struct {
|
|||||||
uint8_t data[];
|
uint8_t data[];
|
||||||
} FelicaListenerReadCommandResponse;
|
} FelicaListenerReadCommandResponse;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FelicaCommandHeaderRaw header;
|
||||||
|
uint8_t data[];
|
||||||
|
} FelicaListServiceCommandResponse;
|
||||||
|
|
||||||
typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;
|
typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;
|
||||||
|
|
||||||
typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;
|
typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;
|
||||||
@@ -254,6 +308,15 @@ void felica_calculate_mac_write(
|
|||||||
const uint8_t* wcnt,
|
const uint8_t* wcnt,
|
||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
uint8_t* mac);
|
uint8_t* mac);
|
||||||
|
|
||||||
|
void felica_write_directory_tree(const FelicaData* data, FuriString* str);
|
||||||
|
|
||||||
|
void felica_get_workflow_type(FelicaData* data);
|
||||||
|
|
||||||
|
void felica_get_ic_name(const FelicaData* data, FuriString* ic_name);
|
||||||
|
|
||||||
|
void felica_service_get_attribute_string(const FelicaService* service, FuriString* str);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
22
lib/nfc/protocols/felica/felica_i.c
Normal file
22
lib/nfc/protocols/felica/felica_i.c
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "felica_i.h"
|
||||||
|
|
||||||
|
const SimpleArrayConfig felica_service_array_cfg = {
|
||||||
|
.init = NULL,
|
||||||
|
.copy = NULL,
|
||||||
|
.reset = NULL,
|
||||||
|
.type_size = sizeof(FelicaService),
|
||||||
|
};
|
||||||
|
|
||||||
|
const SimpleArrayConfig felica_area_array_cfg = {
|
||||||
|
.init = NULL,
|
||||||
|
.copy = NULL,
|
||||||
|
.reset = NULL,
|
||||||
|
.type_size = sizeof(FelicaArea),
|
||||||
|
};
|
||||||
|
|
||||||
|
const SimpleArrayConfig felica_public_block_array_cfg = {
|
||||||
|
.init = NULL,
|
||||||
|
.copy = NULL,
|
||||||
|
.reset = NULL,
|
||||||
|
.type_size = sizeof(FelicaPublicBlock),
|
||||||
|
};
|
||||||
10
lib/nfc/protocols/felica/felica_i.h
Normal file
10
lib/nfc/protocols/felica/felica_i.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "felica.h"
|
||||||
|
|
||||||
|
#include <lib/toolbox/simple_array.h>
|
||||||
|
|
||||||
|
extern const SimpleArrayConfig felica_service_array_cfg;
|
||||||
|
extern const SimpleArrayConfig felica_area_array_cfg;
|
||||||
|
extern const SimpleArrayConfig felica_public_block_array_cfg;
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "felica_poller_i.h"
|
#include "felica_poller_i.h"
|
||||||
|
#include <mlib/m-array.h>
|
||||||
|
#include <mlib/m-core.h>
|
||||||
|
|
||||||
#include <nfc/protocols/nfc_poller_base.h>
|
#include <nfc/protocols/nfc_poller_base.h>
|
||||||
|
|
||||||
@@ -7,6 +9,10 @@
|
|||||||
|
|
||||||
#define TAG "FelicaPoller"
|
#define TAG "FelicaPoller"
|
||||||
|
|
||||||
|
ARRAY_DEF(felica_service_array, FelicaService, M_POD_OPLIST); // -V658
|
||||||
|
ARRAY_DEF(felica_area_array, FelicaArea, M_POD_OPLIST); // -V658
|
||||||
|
ARRAY_DEF(felica_public_block_array, FelicaPublicBlock, M_POD_OPLIST); // -V658
|
||||||
|
|
||||||
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
|
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
|
||||||
|
|
||||||
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
|
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
|
||||||
@@ -79,15 +85,30 @@ NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {
|
|||||||
FelicaError error = felica_poller_activate(instance, instance->data);
|
FelicaError error = felica_poller_activate(instance, instance->data);
|
||||||
if(error == FelicaErrorNone) {
|
if(error == FelicaErrorNone) {
|
||||||
furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE);
|
furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
felica_get_workflow_type(instance->data);
|
||||||
|
|
||||||
instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext;
|
instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext;
|
||||||
instance->felica_event_data.auth_context = &instance->auth.context;
|
instance->felica_event_data.auth_context = &instance->auth.context;
|
||||||
|
|
||||||
instance->callback(instance->general_event, instance->context);
|
instance->callback(instance->general_event, instance->context);
|
||||||
|
|
||||||
|
switch(instance->data->workflow_type) {
|
||||||
|
case FelicaStandard:
|
||||||
|
instance->state = FelicaPollerStateTraverseStandardSystem;
|
||||||
|
break;
|
||||||
|
case FelicaLite:
|
||||||
|
instance->state = FelicaPollerStateReadLiteBlocks;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unimplemented
|
||||||
|
instance->state = FelicaPollerStateReadSuccess;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
bool skip_auth = instance->auth.context.skip_auth;
|
bool skip_auth = instance->auth.context.skip_auth;
|
||||||
instance->state = skip_auth ? FelicaPollerStateReadBlocks :
|
if(!skip_auth) {
|
||||||
FelicaPollerStateAuthenticateInternal;
|
instance->state = FelicaPollerStateAuthenticateInternal;
|
||||||
|
}
|
||||||
} else if(error != FelicaErrorTimeout) {
|
} else if(error != FelicaErrorTimeout) {
|
||||||
instance->felica_event.type = FelicaPollerEventTypeError;
|
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||||
instance->felica_event_data.error = error;
|
instance->felica_event_data.error = error;
|
||||||
@@ -105,7 +126,18 @@ NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
|
|||||||
instance->data->data.fs.rc.data,
|
instance->data->data.fs.rc.data,
|
||||||
instance->auth.session_key.data);
|
instance->auth.session_key.data);
|
||||||
|
|
||||||
instance->state = FelicaPollerStateReadBlocks;
|
switch(instance->data->workflow_type) {
|
||||||
|
case FelicaStandard:
|
||||||
|
instance->state = FelicaPollerStateTraverseStandardSystem;
|
||||||
|
break;
|
||||||
|
case FelicaLite:
|
||||||
|
instance->state = FelicaPollerStateReadLiteBlocks;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unimplemented
|
||||||
|
instance->state = FelicaPollerStateReadSuccess;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0};
|
uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0};
|
||||||
FelicaPollerWriteCommandResponse* tx_resp;
|
FelicaPollerWriteCommandResponse* tx_resp;
|
||||||
@@ -145,7 +177,6 @@ NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
|
|||||||
|
|
||||||
NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
|
NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
|
||||||
FURI_LOG_D(TAG, "Auth External");
|
FURI_LOG_D(TAG, "Auth External");
|
||||||
instance->state = FelicaPollerStateReadBlocks;
|
|
||||||
uint8_t blocks[2];
|
uint8_t blocks[2];
|
||||||
|
|
||||||
instance->data->data.fs.state.data[0] = 1;
|
instance->data->data.fs.state.data[0] = 1;
|
||||||
@@ -183,12 +214,177 @@ NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
|
|||||||
memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE);
|
memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE);
|
||||||
instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];
|
instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];
|
||||||
} while(false);
|
} while(false);
|
||||||
instance->state = FelicaPollerStateReadBlocks;
|
|
||||||
|
switch(instance->data->workflow_type) {
|
||||||
|
case FelicaStandard:
|
||||||
|
instance->state = FelicaPollerStateTraverseStandardSystem;
|
||||||
|
break;
|
||||||
|
case FelicaLite:
|
||||||
|
instance->state = FelicaPollerStateReadLiteBlocks;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unimplemented
|
||||||
|
instance->state = FelicaPollerStateReadSuccess;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return NfcCommandContinue;
|
return NfcCommandContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
NfcCommand felica_poller_state_handler_read_blocks(FelicaPoller* instance) {
|
NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* instance) {
|
||||||
FURI_LOG_D(TAG, "Read Blocks");
|
FURI_LOG_D(TAG, "Traverse Standard System");
|
||||||
|
|
||||||
|
FelicaListServiceCommandResponse* response;
|
||||||
|
|
||||||
|
felica_service_array_t service_buffer;
|
||||||
|
felica_service_array_init(service_buffer);
|
||||||
|
felica_area_array_t area_buffer;
|
||||||
|
felica_area_array_init(area_buffer);
|
||||||
|
|
||||||
|
for(uint16_t cursor = 0; cursor < 0xFFFF; cursor++) {
|
||||||
|
FelicaError error = felica_poller_list_service_by_cursor(instance, cursor, &response);
|
||||||
|
if(error != FelicaErrorNone) {
|
||||||
|
FURI_LOG_E(TAG, "Error %d at cursor %04X", error, cursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t len = response->header.length;
|
||||||
|
const uint8_t* list_service_payload = response->data;
|
||||||
|
uint16_t code_begin = (uint16_t)(list_service_payload[0] | (list_service_payload[1] << 8));
|
||||||
|
|
||||||
|
if(len != 0x0C && len != 0x0E) {
|
||||||
|
FURI_LOG_E(TAG, "Bad command resp length 0x%02X at cursor 0x%04X", len, cursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(code_begin == 0xFFFF) {
|
||||||
|
FURI_LOG_D(TAG, "Traverse complete");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(len == 0x0E) {
|
||||||
|
FelicaArea* area = felica_area_array_push_raw(area_buffer);
|
||||||
|
memset(area, 0, sizeof *area);
|
||||||
|
area->code = code_begin;
|
||||||
|
area->first_idx = (uint16_t)felica_service_array_size(service_buffer);
|
||||||
|
area->last_idx = 0;
|
||||||
|
} else {
|
||||||
|
FelicaService* service = felica_service_array_push_raw(service_buffer);
|
||||||
|
memset(service, 0, sizeof *service);
|
||||||
|
service->code = code_begin;
|
||||||
|
service->attr = (uint8_t)(code_begin & 0x3F);
|
||||||
|
|
||||||
|
if(felica_area_array_size(area_buffer)) {
|
||||||
|
FelicaArea* current_area = felica_area_array_back(area_buffer);
|
||||||
|
current_area->last_idx = (uint16_t)(felica_service_array_size(service_buffer) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t service_num = felica_service_array_size(service_buffer);
|
||||||
|
const size_t area_num = felica_area_array_size(area_buffer);
|
||||||
|
|
||||||
|
if(service_num) {
|
||||||
|
simple_array_init(instance->data->services, (uint32_t)service_num);
|
||||||
|
memcpy(
|
||||||
|
simple_array_get(instance->data->services, 0),
|
||||||
|
service_buffer->ptr,
|
||||||
|
service_num * sizeof(FelicaService));
|
||||||
|
} else {
|
||||||
|
simple_array_reset(instance->data->services);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(area_num) {
|
||||||
|
simple_array_init(instance->data->areas, (uint32_t)area_num);
|
||||||
|
memcpy(
|
||||||
|
simple_array_get(instance->data->areas, 0),
|
||||||
|
area_buffer->ptr,
|
||||||
|
area_num * sizeof(FelicaArea));
|
||||||
|
} else {
|
||||||
|
simple_array_reset(instance->data->areas);
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"Services found: %lu, Areas found: %lu",
|
||||||
|
simple_array_get_count(instance->data->services),
|
||||||
|
simple_array_get_count(instance->data->areas));
|
||||||
|
|
||||||
|
felica_service_array_clear(service_buffer);
|
||||||
|
felica_area_array_clear(area_buffer);
|
||||||
|
|
||||||
|
instance->state = FelicaPollerStateReadStandardBlocks;
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Read Standard Blocks");
|
||||||
|
|
||||||
|
const uint32_t service_count = simple_array_get_count(instance->data->services);
|
||||||
|
|
||||||
|
felica_public_block_array_t public_block_buffer;
|
||||||
|
felica_public_block_array_init(public_block_buffer);
|
||||||
|
|
||||||
|
instance->state = FelicaPollerStateReadSuccess;
|
||||||
|
bool have_read_anything = false;
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < service_count; i++) {
|
||||||
|
const FelicaService* service = simple_array_get(instance->data->services, i);
|
||||||
|
|
||||||
|
if((service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 0) continue;
|
||||||
|
|
||||||
|
uint8_t block_count = 1;
|
||||||
|
uint8_t block_list[1] = {0};
|
||||||
|
FelicaError error = FelicaErrorNone;
|
||||||
|
FelicaPollerReadCommandResponse* response;
|
||||||
|
do {
|
||||||
|
error = felica_poller_read_blocks(
|
||||||
|
instance, block_count, block_list, service->code, &response);
|
||||||
|
|
||||||
|
if(error != FelicaErrorNone) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(response->SF1 == 0 && response->SF2 == 0) {
|
||||||
|
FelicaPublicBlock* public_block =
|
||||||
|
felica_public_block_array_push_raw(public_block_buffer);
|
||||||
|
memset(public_block, 0, sizeof *public_block);
|
||||||
|
memcpy(public_block->block.data, response->data, FELICA_DATA_BLOCK_SIZE);
|
||||||
|
|
||||||
|
public_block->service_code = service->code;
|
||||||
|
public_block->block_idx = block_list[0];
|
||||||
|
|
||||||
|
have_read_anything = true;
|
||||||
|
block_list[0]++;
|
||||||
|
} else {
|
||||||
|
break; // No more blocks to read in this service, ok to continue for loop
|
||||||
|
}
|
||||||
|
} while(block_list[0] < FELICA_STANDARD_MAX_BLOCK_COUNT);
|
||||||
|
|
||||||
|
if(error != FelicaErrorNone) {
|
||||||
|
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||||
|
instance->felica_event_data.error = error;
|
||||||
|
instance->state = FelicaPollerStateReadFailed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(have_read_anything) {
|
||||||
|
const size_t n = felica_public_block_array_size(public_block_buffer);
|
||||||
|
simple_array_init(instance->data->public_blocks, (uint32_t)n);
|
||||||
|
memcpy(
|
||||||
|
simple_array_get(instance->data->public_blocks, 0),
|
||||||
|
public_block_buffer->ptr,
|
||||||
|
n * sizeof(FelicaPublicBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
felica_public_block_array_clear(public_block_buffer);
|
||||||
|
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfcCommand felica_poller_state_handler_read_lite_blocks(FelicaPoller* instance) {
|
||||||
|
FURI_LOG_D(TAG, "Read Lite Blocks");
|
||||||
|
|
||||||
uint8_t block_count = 1;
|
uint8_t block_count = 1;
|
||||||
uint8_t block_list[4] = {0, 0, 0, 0};
|
uint8_t block_list[4] = {0, 0, 0, 0};
|
||||||
@@ -266,7 +462,10 @@ static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum]
|
|||||||
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
||||||
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
||||||
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
|
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
|
||||||
[FelicaPollerStateReadBlocks] = felica_poller_state_handler_read_blocks,
|
[FelicaPollerStateTraverseStandardSystem] =
|
||||||
|
felica_poller_state_handler_traverse_standard_system,
|
||||||
|
[FelicaPollerStateReadStandardBlocks] = felica_poller_state_handler_read_standard_blocks,
|
||||||
|
[FelicaPollerStateReadLiteBlocks] = felica_poller_state_handler_read_lite_blocks,
|
||||||
[FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success,
|
[FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success,
|
||||||
[FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,
|
[FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ FelicaError felica_poller_polling(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is in fact a buffer preparer for a specified service. It should be have the _ex suffix. The prepare_tx_buffer_raw should have this name.
|
||||||
static void felica_poller_prepare_tx_buffer(
|
static void felica_poller_prepare_tx_buffer(
|
||||||
const FelicaPoller* instance,
|
const FelicaPoller* instance,
|
||||||
const uint8_t command,
|
const uint8_t command,
|
||||||
@@ -221,3 +222,45 @@ FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void felica_poller_prepare_tx_buffer_raw(
|
||||||
|
const FelicaPoller* instance,
|
||||||
|
const uint8_t command,
|
||||||
|
const uint8_t* data,
|
||||||
|
const uint8_t data_length) {
|
||||||
|
FelicaCommandHeaderRaw cmd = {.length = 0x00, .command = command, .idm = instance->data->idm};
|
||||||
|
|
||||||
|
cmd.length = sizeof(FelicaCommandHeaderRaw) + data_length;
|
||||||
|
bit_buffer_reset(instance->tx_buffer);
|
||||||
|
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeaderRaw));
|
||||||
|
bit_buffer_append_bytes(instance->tx_buffer, data, data_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
FelicaError felica_poller_list_service_by_cursor(
|
||||||
|
FelicaPoller* instance,
|
||||||
|
uint16_t cursor,
|
||||||
|
FelicaListServiceCommandResponse** const response_ptr) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(response_ptr);
|
||||||
|
|
||||||
|
const uint8_t data[2] = {(uint8_t)(cursor & 0xFF), (uint8_t)((cursor >> 8) & 0xFF)};
|
||||||
|
|
||||||
|
felica_poller_prepare_tx_buffer_raw(
|
||||||
|
instance, FELICA_CMD_LIST_SERVICE_CODE, data, sizeof(data));
|
||||||
|
|
||||||
|
bit_buffer_reset(instance->rx_buffer);
|
||||||
|
|
||||||
|
FelicaError error = felica_poller_frame_exchange(
|
||||||
|
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
|
||||||
|
if(error != FelicaErrorNone) {
|
||||||
|
FURI_LOG_E(TAG, "List service by cursor failed with error: %d", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer);
|
||||||
|
if(rx_len < sizeof(FelicaCommandHeaderRaw) + 2) return FelicaErrorProtocol;
|
||||||
|
|
||||||
|
// error is known to be FelicaErrorNone here
|
||||||
|
*response_ptr = (FelicaListServiceCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ typedef enum {
|
|||||||
FelicaPollerStateActivated,
|
FelicaPollerStateActivated,
|
||||||
FelicaPollerStateAuthenticateInternal,
|
FelicaPollerStateAuthenticateInternal,
|
||||||
FelicaPollerStateAuthenticateExternal,
|
FelicaPollerStateAuthenticateExternal,
|
||||||
FelicaPollerStateReadBlocks,
|
FelicaPollerStateTraverseStandardSystem,
|
||||||
|
FelicaPollerStateReadStandardBlocks,
|
||||||
|
FelicaPollerStateReadLiteBlocks,
|
||||||
FelicaPollerStateReadSuccess,
|
FelicaPollerStateReadSuccess,
|
||||||
FelicaPollerStateReadFailed,
|
FelicaPollerStateReadFailed,
|
||||||
|
|
||||||
@@ -55,6 +57,10 @@ typedef struct {
|
|||||||
uint8_t request_data[2];
|
uint8_t request_data[2];
|
||||||
} FelicaPollerPollingResponse;
|
} FelicaPollerPollingResponse;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
FelicaData* data;
|
||||||
|
} FelicaPollerContextData;
|
||||||
|
|
||||||
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
|
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,6 +111,11 @@ FelicaError felica_poller_frame_exchange(
|
|||||||
BitBuffer* rx_buffer,
|
BitBuffer* rx_buffer,
|
||||||
uint32_t fwt);
|
uint32_t fwt);
|
||||||
|
|
||||||
|
FelicaError felica_poller_list_service_by_cursor(
|
||||||
|
FelicaPoller* instance,
|
||||||
|
uint16_t cursor,
|
||||||
|
FelicaListServiceCommandResponse** response_ptr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ typedef struct {
|
|||||||
FelicaAuthenticationContext auth_ctx;
|
FelicaAuthenticationContext auth_ctx;
|
||||||
FuriThreadId thread_id;
|
FuriThreadId thread_id;
|
||||||
FelicaError error;
|
FelicaError error;
|
||||||
FelicaData data;
|
FelicaPollerContextData data;
|
||||||
} Felica_PollerContext;
|
} FelicaPollerContext;
|
||||||
|
|
||||||
NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {
|
NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@@ -20,14 +20,14 @@ NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {
|
|||||||
furi_assert(event.instance);
|
furi_assert(event.instance);
|
||||||
furi_assert(event.protocol == NfcProtocolFelica);
|
furi_assert(event.protocol == NfcProtocolFelica);
|
||||||
|
|
||||||
Felica_PollerContext* poller_context = context;
|
FelicaPollerContext* poller_context = context;
|
||||||
FelicaPoller* felica_poller = event.instance;
|
FelicaPoller* felica_poller = event.instance;
|
||||||
|
|
||||||
FelicaPollerEvent* felica_event = event.event_data;
|
FelicaPollerEvent* felica_event = event.event_data;
|
||||||
|
|
||||||
if(felica_event->type == FelicaPollerEventTypeReady ||
|
if(felica_event->type == FelicaPollerEventTypeReady ||
|
||||||
felica_event->type == FelicaPollerEventTypeIncomplete) {
|
felica_event->type == FelicaPollerEventTypeIncomplete) {
|
||||||
felica_copy(&poller_context->data, felica_poller->data);
|
felica_copy(poller_context->data.data, felica_poller->data);
|
||||||
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
|
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
|
||||||
felica_event->data->auth_context->skip_auth = poller_context->auth_ctx.skip_auth;
|
felica_event->data->auth_context->skip_auth = poller_context->auth_ctx.skip_auth;
|
||||||
memcpy(
|
memcpy(
|
||||||
@@ -45,7 +45,7 @@ FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCard
|
|||||||
furi_check(nfc);
|
furi_check(nfc);
|
||||||
furi_check(data);
|
furi_check(data);
|
||||||
|
|
||||||
Felica_PollerContext poller_context = {};
|
FelicaPollerContext poller_context = {};
|
||||||
if(card_key == NULL) {
|
if(card_key == NULL) {
|
||||||
poller_context.auth_ctx.skip_auth = true;
|
poller_context.auth_ctx.skip_auth = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -54,6 +54,7 @@ FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
poller_context.thread_id = furi_thread_get_current_id();
|
poller_context.thread_id = furi_thread_get_current_id();
|
||||||
|
poller_context.data.data = felica_alloc();
|
||||||
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolFelica);
|
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolFelica);
|
||||||
nfc_poller_start(poller, felica_poller_read_callback, &poller_context);
|
nfc_poller_start(poller, felica_poller_read_callback, &poller_context);
|
||||||
furi_thread_flags_wait(FELICA_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
|
furi_thread_flags_wait(FELICA_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
|
||||||
@@ -63,8 +64,10 @@ FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCard
|
|||||||
nfc_poller_free(poller);
|
nfc_poller_free(poller);
|
||||||
|
|
||||||
if(poller_context.error == FelicaErrorNone) {
|
if(poller_context.error == FelicaErrorNone) {
|
||||||
*data = poller_context.data;
|
felica_copy(data, poller_context.data.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
felica_free(poller_context.data.data);
|
||||||
|
|
||||||
return poller_context.error;
|
return poller_context.error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ SubGhzProtocolStatus
|
|||||||
}
|
}
|
||||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||||
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||||
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF;
|
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;
|
||||||
}
|
}
|
||||||
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
|
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
|
||||||
FURI_LOG_E(TAG, "Unable to add Key");
|
FURI_LOG_E(TAG, "Unable to add Key");
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ SubGhzProtocolStatus
|
|||||||
}
|
}
|
||||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||||
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||||
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF;
|
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;
|
||||||
}
|
}
|
||||||
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
|
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
|
||||||
FURI_LOG_E(TAG, "Unable to add Key");
|
FURI_LOG_E(TAG, "Unable to add Key");
|
||||||
|
|||||||
@@ -249,13 +249,8 @@ static void subghz_protocol_scher_khan_check_remote_controller(
|
|||||||
instance->btn = 0;
|
instance->btn = 0;
|
||||||
instance->cnt = 0;
|
instance->cnt = 0;
|
||||||
break;
|
break;
|
||||||
case 81: //MAGIC CODE PRO / PRO2 Response ???
|
case 81: // MAGIC CODE PRO / PRO2 Response ???
|
||||||
*protocol_name = "MAGIC CODE PRO,\n Response";
|
case 82: // MAGIC CODE PRO / PRO2 Response ???
|
||||||
instance->serial = 0;
|
|
||||||
instance->btn = 0;
|
|
||||||
instance->cnt = 0;
|
|
||||||
break;
|
|
||||||
case 82: //MAGIC CODE PRO / PRO2 Response ???
|
|
||||||
*protocol_name = "MAGIC CODE PRO,\n Response";
|
*protocol_name = "MAGIC CODE PRO,\n Response";
|
||||||
instance->serial = 0;
|
instance->serial = 0;
|
||||||
instance->btn = 0;
|
instance->btn = 0;
|
||||||
|
|||||||
@@ -1070,7 +1070,9 @@ 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"
|
||||||
|
Function,+,felica_get_ic_name,void,"const FelicaData*, FuriString*"
|
||||||
Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*"
|
Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*"
|
||||||
|
Function,+,felica_get_workflow_type,void,FelicaData*
|
||||||
Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*"
|
Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*"
|
||||||
Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t"
|
Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t"
|
||||||
Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*"
|
Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*"
|
||||||
@@ -1078,8 +1080,10 @@ Function,+,felica_poller_read_blocks,FelicaError,"FelicaPoller*, const uint8_t,
|
|||||||
Function,+,felica_poller_sync_read,FelicaError,"Nfc*, FelicaData*, const FelicaCardKey*"
|
Function,+,felica_poller_sync_read,FelicaError,"Nfc*, FelicaData*, const FelicaCardKey*"
|
||||||
Function,+,felica_reset,void,FelicaData*
|
Function,+,felica_reset,void,FelicaData*
|
||||||
Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*"
|
Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*"
|
||||||
|
Function,+,felica_service_get_attribute_string,void,"const FelicaService*, FuriString*"
|
||||||
Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t"
|
Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t"
|
||||||
Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*"
|
Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*"
|
||||||
|
Function,+,felica_write_directory_tree,void,"const FelicaData*, FuriString*"
|
||||||
Function,-,feof,int,FILE*
|
Function,-,feof,int,FILE*
|
||||||
Function,-,feof_unlocked,int,FILE*
|
Function,-,feof_unlocked,int,FILE*
|
||||||
Function,-,ferror,int,FILE*
|
Function,-,ferror,int,FILE*
|
||||||
|
|||||||
|
Reference in New Issue
Block a user