mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
Merge remote-tracking branch 'OFW/dev' into dev
This commit is contained in:
@@ -7,7 +7,7 @@ UID: 29 9F FA 53 AB 75 87 6E
|
||||
# FeliCa specific data
|
||||
Data format version: 1
|
||||
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 read: 28
|
||||
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) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
nfc_render_felica_dump(data, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
// Jump to advanced scene right away
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);
|
||||
}
|
||||
|
||||
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {
|
||||
|
||||
@@ -4,9 +4,16 @@ void nfc_render_felica_blocks_count(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
bool render_auth_notification) {
|
||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||
if(data->workflow_type == FelicaLite) {
|
||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||
|
||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||
}
|
||||
} else if(data->workflow_type == FelicaStandard) {
|
||||
furi_string_cat_printf(
|
||||
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +39,11 @@ void nfc_render_felica_info(
|
||||
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
||||
}
|
||||
|
||||
FuriString* ic_type_str = furi_string_alloc();
|
||||
felica_get_ic_name(data, ic_type_str);
|
||||
furi_string_cat_printf(str, "IC Type:\n%s\n", furi_string_get_cstr(ic_type_str));
|
||||
furi_string_free(ic_type_str);
|
||||
|
||||
nfc_render_felica_idm(data, format_type, str);
|
||||
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
@@ -40,6 +52,14 @@ void nfc_render_felica_info(
|
||||
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n");
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"Services found: %lu \nAreas found: %lu\n",
|
||||
simple_array_get_count(data->services),
|
||||
simple_array_get_count(data->areas));
|
||||
|
||||
nfc_render_felica_blocks_count(data, str, true);
|
||||
}
|
||||
|
||||
@@ -59,13 +79,18 @@ static void nfc_render_felica_block_name(
|
||||
|
||||
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
||||
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
|
||||
furi_string_cat_printf(str, "%02X ", block->data[j]);
|
||||
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
|
||||
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
|
||||
}
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {
|
||||
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
|
||||
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block(
|
||||
const FelicaBlock* block,
|
||||
FuriString* str,
|
||||
@@ -76,8 +101,13 @@ static void nfc_render_felica_block(
|
||||
nfc_render_felica_block_data(block, str);
|
||||
}
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {
|
||||
FuriString* name = furi_string_alloc();
|
||||
|
||||
furi_string_cat_printf(str, "\e#Blocks read:\n");
|
||||
|
||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||
|
||||
for(size_t i = 0; i < 14; i++) {
|
||||
furi_string_printf(name, "S_PAD%d", i);
|
||||
uint8_t suf_cnt = 18;
|
||||
@@ -105,3 +135,70 @@ void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||
}
|
||||
|
||||
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
|
||||
const size_t area_count = simple_array_get_count(data->areas);
|
||||
const size_t service_count = simple_array_get_count(data->services);
|
||||
|
||||
furi_string_cat_printf(str, "\e#Directory Tree:\n");
|
||||
|
||||
if(area_count == 0 || service_count == 0) {
|
||||
furi_string_cat_printf(str, "No services or areas found.\n");
|
||||
} else {
|
||||
furi_string_cat_printf(
|
||||
str, "%zu areas found.\n%zu services found.\n\n", area_count, service_count);
|
||||
furi_string_cat_printf(
|
||||
str, "::: ... are readable services\n||| ... are locked services\n");
|
||||
}
|
||||
felica_write_directory_tree(data, str);
|
||||
}
|
||||
|
||||
void nfc_more_info_render_felica_blocks(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
const uint16_t service_code_key) {
|
||||
furi_string_cat_printf(str, "\n");
|
||||
if(data->workflow_type == FelicaLite) {
|
||||
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
|
||||
FuriString* name = furi_string_alloc();
|
||||
|
||||
for(size_t i = 0; i < 14; i++) {
|
||||
furi_string_printf(name, "S_PAD%d", i);
|
||||
uint8_t suf_cnt = 18;
|
||||
if(i == 1) {
|
||||
suf_cnt = 19;
|
||||
} else if((i == 10) || (i == 12) || (i == 13)) {
|
||||
suf_cnt = 16;
|
||||
}
|
||||
nfc_render_felica_block(
|
||||
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
|
||||
}
|
||||
furi_string_free(name);
|
||||
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
|
||||
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
|
||||
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
|
||||
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
|
||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||
|
||||
} else if(data->workflow_type == FelicaStandard) {
|
||||
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
|
||||
for(size_t i = 0; i < public_blocks_count; i++) {
|
||||
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
|
||||
if(public_block->service_code != service_code_key) {
|
||||
continue; // Skip blocks not matching the requested service code
|
||||
}
|
||||
furi_string_cat_printf(str, "-----Block 0x%02X-----\n", public_block->block_idx);
|
||||
nfc_render_felica_block_data_simple(&public_block->block, str);
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,16 @@ void nfc_render_felica_info(
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
|
||||
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);
|
||||
|
||||
void nfc_render_felica_idm(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);
|
||||
|
||||
void nfc_more_info_render_felica_blocks(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
const uint16_t service_code_key);
|
||||
|
||||
@@ -69,4 +69,6 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput)
|
||||
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
|
||||
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
|
||||
|
||||
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
|
||||
|
||||
ADD_SCENE(nfc, generate_info, GenerateInfo)
|
||||
|
||||
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>
|
||||
|
||||
@@ -11,7 +12,7 @@
|
||||
#define FELICA_MANUFACTURE_ID "Manufacture id"
|
||||
#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
|
||||
* type of block needs to be prepared.
|
||||
@@ -39,24 +40,71 @@ const NfcDeviceBase nfc_device_felica = {
|
||||
|
||||
FelicaData* felica_alloc(void) {
|
||||
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;
|
||||
}
|
||||
|
||||
void felica_free(FelicaData* 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);
|
||||
}
|
||||
|
||||
void felica_reset(FelicaData* 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) {
|
||||
furi_check(data);
|
||||
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) {
|
||||
@@ -70,43 +118,164 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_check(data);
|
||||
|
||||
bool parsed = false;
|
||||
FuriString* str_key_buffer = furi_string_alloc();
|
||||
FuriString* str_data_buffer = furi_string_alloc();
|
||||
|
||||
// Header
|
||||
do {
|
||||
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
|
||||
|
||||
uint32_t data_format_version = 0;
|
||||
if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))
|
||||
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))
|
||||
break;
|
||||
if(!flipper_format_read_hex(
|
||||
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
||||
break;
|
||||
|
||||
parsed = true;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
felica_get_workflow_type(data);
|
||||
if(data_format_version == 1) {
|
||||
data->workflow_type = FelicaLite;
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
parsed = true;
|
||||
} 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;
|
||||
}
|
||||
|
||||
@@ -114,8 +283,10 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
||||
furi_check(data);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
FuriString* str_data_buffer = furi_string_alloc();
|
||||
FuriString* str_key_buffer = furi_string_alloc();
|
||||
do {
|
||||
// Header
|
||||
if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break;
|
||||
if(!flipper_format_write_uint32(
|
||||
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))
|
||||
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;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
for(uint8_t i = 0; i < blocks_total; i++) {
|
||||
furi_string_printf(temp_str, "Block %d", i);
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
furi_string_get_cstr(temp_str),
|
||||
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||
sizeof(FelicaBlock))) {
|
||||
saved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
felica_get_ic_name(data, str_data_buffer);
|
||||
furi_string_replace_all(str_data_buffer, "\n", " ");
|
||||
if(!flipper_format_write_string(ff, "IC Type", str_data_buffer)) break;
|
||||
if(!flipper_format_write_empty_line(ff)) break;
|
||||
} 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;
|
||||
}
|
||||
|
||||
@@ -154,7 +432,13 @@ bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
|
||||
furi_check(data);
|
||||
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) {
|
||||
@@ -355,3 +639,251 @@ void felica_calculate_mac_write(
|
||||
memcpy(session_swapped + 8, session_key, 8);
|
||||
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 <nfc/protocols/nfc_device_base_i.h>
|
||||
#include <mbedtls/include/mbedtls/des.h>
|
||||
#include <lib/toolbox/simple_array.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -34,6 +35,8 @@ extern "C" {
|
||||
#define FELICA_BLOCK_INDEX_STATE (0x92U)
|
||||
#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)
|
||||
|
||||
#define FELICA_STANDARD_MAX_BLOCK_COUNT (0xFFU)
|
||||
|
||||
#define FELICA_GUARD_TIME_US (20000U)
|
||||
#define FELICA_FDT_POLL_FC (10000U)
|
||||
#define FELICA_POLL_POLL_MIN_US (1280U)
|
||||
@@ -47,6 +50,16 @@ extern "C" {
|
||||
#define FELICA_TIME_SLOT_8 (0x07U)
|
||||
#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 */
|
||||
typedef enum {
|
||||
FelicaErrorNone,
|
||||
@@ -61,6 +74,12 @@ typedef enum {
|
||||
FelicaErrorFeatureUnsupported,
|
||||
} FelicaError;
|
||||
|
||||
typedef enum {
|
||||
FelicaUnknown,
|
||||
FelicaStandard,
|
||||
FelicaLite,
|
||||
} FelicaWorkflowType;
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[FELICA_DATA_BLOCK_SIZE];
|
||||
} FelicaBlockData;
|
||||
@@ -146,6 +165,23 @@ typedef union {
|
||||
uint8_t dump[sizeof(FelicaFileSystem)];
|
||||
} 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 */
|
||||
typedef struct {
|
||||
FelicaIDm idm;
|
||||
@@ -153,6 +189,11 @@ typedef struct {
|
||||
uint8_t blocks_total;
|
||||
uint8_t blocks_read;
|
||||
FelicaFSUnion data;
|
||||
|
||||
SimpleArray* services;
|
||||
SimpleArray* areas;
|
||||
SimpleArray* public_blocks;
|
||||
FelicaWorkflowType workflow_type;
|
||||
} FelicaData;
|
||||
|
||||
typedef struct FURI_PACKED {
|
||||
@@ -171,6 +212,14 @@ typedef struct {
|
||||
uint8_t SF2;
|
||||
} FelicaCommandResponseHeader;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint8_t length;
|
||||
uint8_t command;
|
||||
FelicaIDm idm;
|
||||
} FelicaCommandHeaderRaw;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct {
|
||||
uint8_t service_code : 4;
|
||||
uint8_t access_mode : 3;
|
||||
@@ -194,6 +243,11 @@ typedef struct {
|
||||
uint8_t data[];
|
||||
} FelicaListenerReadCommandResponse;
|
||||
|
||||
typedef struct {
|
||||
FelicaCommandHeaderRaw header;
|
||||
uint8_t data[];
|
||||
} FelicaListServiceCommandResponse;
|
||||
|
||||
typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;
|
||||
|
||||
typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;
|
||||
@@ -254,6 +308,15 @@ void felica_calculate_mac_write(
|
||||
const uint8_t* wcnt,
|
||||
const uint8_t* data,
|
||||
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
|
||||
}
|
||||
#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 <mlib/m-array.h>
|
||||
#include <mlib/m-core.h>
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
@@ -7,6 +9,10 @@
|
||||
|
||||
#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);
|
||||
|
||||
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);
|
||||
if(error == FelicaErrorNone) {
|
||||
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_data.auth_context = &instance->auth.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;
|
||||
instance->state = skip_auth ? FelicaPollerStateReadBlocks :
|
||||
FelicaPollerStateAuthenticateInternal;
|
||||
if(!skip_auth) {
|
||||
instance->state = FelicaPollerStateAuthenticateInternal;
|
||||
}
|
||||
} else if(error != FelicaErrorTimeout) {
|
||||
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||
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->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};
|
||||
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) {
|
||||
FURI_LOG_D(TAG, "Auth External");
|
||||
instance->state = FelicaPollerStateReadBlocks;
|
||||
uint8_t blocks[2];
|
||||
|
||||
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);
|
||||
instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];
|
||||
} 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;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_read_blocks(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read Blocks");
|
||||
NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* instance) {
|
||||
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_list[4] = {0, 0, 0, 0};
|
||||
@@ -266,7 +462,10 @@ static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum]
|
||||
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
||||
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
||||
[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,
|
||||
[FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,
|
||||
};
|
||||
|
||||
@@ -93,6 +93,7 @@ FelicaError felica_poller_polling(
|
||||
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(
|
||||
const FelicaPoller* instance,
|
||||
const uint8_t command,
|
||||
@@ -221,3 +222,45 @@ FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {
|
||||
|
||||
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,
|
||||
FelicaPollerStateAuthenticateInternal,
|
||||
FelicaPollerStateAuthenticateExternal,
|
||||
FelicaPollerStateReadBlocks,
|
||||
FelicaPollerStateTraverseStandardSystem,
|
||||
FelicaPollerStateReadStandardBlocks,
|
||||
FelicaPollerStateReadLiteBlocks,
|
||||
FelicaPollerStateReadSuccess,
|
||||
FelicaPollerStateReadFailed,
|
||||
|
||||
@@ -55,6 +57,10 @@ typedef struct {
|
||||
uint8_t request_data[2];
|
||||
} FelicaPollerPollingResponse;
|
||||
|
||||
typedef union {
|
||||
FelicaData* data;
|
||||
} FelicaPollerContextData;
|
||||
|
||||
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
|
||||
|
||||
/**
|
||||
@@ -105,6 +111,11 @@ FelicaError felica_poller_frame_exchange(
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt);
|
||||
|
||||
FelicaError felica_poller_list_service_by_cursor(
|
||||
FelicaPoller* instance,
|
||||
uint16_t cursor,
|
||||
FelicaListServiceCommandResponse** response_ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -11,8 +11,8 @@ typedef struct {
|
||||
FelicaAuthenticationContext auth_ctx;
|
||||
FuriThreadId thread_id;
|
||||
FelicaError error;
|
||||
FelicaData data;
|
||||
} Felica_PollerContext;
|
||||
FelicaPollerContextData data;
|
||||
} FelicaPollerContext;
|
||||
|
||||
NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
@@ -20,14 +20,14 @@ NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.instance);
|
||||
furi_assert(event.protocol == NfcProtocolFelica);
|
||||
|
||||
Felica_PollerContext* poller_context = context;
|
||||
FelicaPollerContext* poller_context = context;
|
||||
FelicaPoller* felica_poller = event.instance;
|
||||
|
||||
FelicaPollerEvent* felica_event = event.event_data;
|
||||
|
||||
if(felica_event->type == FelicaPollerEventTypeReady ||
|
||||
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) {
|
||||
felica_event->data->auth_context->skip_auth = poller_context->auth_ctx.skip_auth;
|
||||
memcpy(
|
||||
@@ -45,7 +45,7 @@ FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCard
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
Felica_PollerContext poller_context = {};
|
||||
FelicaPollerContext poller_context = {};
|
||||
if(card_key == NULL) {
|
||||
poller_context.auth_ctx.skip_auth = true;
|
||||
} 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.data.data = felica_alloc();
|
||||
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolFelica);
|
||||
nfc_poller_start(poller, felica_poller_read_callback, &poller_context);
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
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))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Key");
|
||||
|
||||
@@ -267,7 +267,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
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))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Key");
|
||||
|
||||
@@ -250,13 +250,8 @@ static void subghz_protocol_scher_khan_check_remote_controller(
|
||||
instance->btn = 0;
|
||||
instance->cnt = 0;
|
||||
break;
|
||||
case 81: //MAGIC CODE PRO / PRO2 Response ???
|
||||
*protocol_name = "MAGIC CODE PRO,\n Response";
|
||||
instance->serial = 0;
|
||||
instance->btn = 0;
|
||||
instance->cnt = 0;
|
||||
break;
|
||||
case 82: //MAGIC CODE PRO / PRO2 Response ???
|
||||
case 81: // MAGIC CODE PRO / PRO2 Response ???
|
||||
case 82: // MAGIC CODE PRO / PRO2 Response ???
|
||||
*protocol_name = "MAGIC CODE PRO,\n Response";
|
||||
instance->serial = 0;
|
||||
instance->btn = 0;
|
||||
|
||||
@@ -1106,7 +1106,9 @@ Function,+,felica_crc_trim,void,BitBuffer*
|
||||
Function,+,felica_free,void,FelicaData*
|
||||
Function,+,felica_get_base_data,FelicaData*,const FelicaData*
|
||||
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_workflow_type,void,FelicaData*
|
||||
Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*"
|
||||
Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t"
|
||||
Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*"
|
||||
@@ -1114,8 +1116,10 @@ Function,+,felica_poller_read_blocks,FelicaError,"FelicaPoller*, const uint8_t,
|
||||
Function,+,felica_poller_sync_read,FelicaError,"Nfc*, FelicaData*, const FelicaCardKey*"
|
||||
Function,+,felica_reset,void,FelicaData*
|
||||
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_verify,_Bool,"FelicaData*, const FuriString*"
|
||||
Function,+,felica_write_directory_tree,void,"const FelicaData*, FuriString*"
|
||||
Function,-,feof,int,FILE*
|
||||
Function,-,feof_unlocked,int,FILE*
|
||||
Function,-,ferror,int,FILE*
|
||||
|
||||
|
Reference in New Issue
Block a user