Skip to content

Commit 85b6b2b

Browse files
zinonglihedger
andauthored
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 ee3f7bf. * 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 <[email protected]> Co-authored-by: hedger <[email protected]>
1 parent fad487d commit 85b6b2b

File tree

18 files changed

+1217
-87
lines changed

18 files changed

+1217
-87
lines changed

applications/debug/unit_tests/resources/unit_tests/nfc/Felica.nfc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ UID: 29 9F FA 53 AB 75 87 6E
77
# FeliCa specific data
88
Data format version: 1
99
Manufacture id: 29 9F FA 53 AB 75 87 6E
10-
Manufacture parameter: 57 4E 10 2A 94 16 BC 8E
10+
Manufacture parameter: 00 F1 00 00 00 01 43 00
1111
Blocks total: 28
1212
Blocks read: 28
1313
Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF

applications/main/nfc/helpers/protocol_support/felica/felica.c

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,8 @@ static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent e
3939
}
4040

4141
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
42-
const NfcDevice* device = instance->nfc_device;
43-
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
44-
45-
FuriString* temp_str = furi_string_alloc();
46-
47-
nfc_render_felica_dump(data, temp_str);
48-
49-
widget_add_text_scroll_element(
50-
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
51-
52-
furi_string_free(temp_str);
42+
// Jump to advanced scene right away
43+
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);
5344
}
5445

5546
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {

applications/main/nfc/helpers/protocol_support/felica/felica_render.c

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@ void nfc_render_felica_blocks_count(
44
const FelicaData* data,
55
FuriString* str,
66
bool render_auth_notification) {
7-
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
8-
if(render_auth_notification && data->blocks_read != data->blocks_total) {
9-
furi_string_cat_printf(str, "\nAuth-protected blocks!");
7+
if(data->workflow_type == FelicaLite) {
8+
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
9+
10+
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
11+
if(render_auth_notification && data->blocks_read != data->blocks_total) {
12+
furi_string_cat_printf(str, "\nAuth-protected blocks!");
13+
}
14+
} else if(data->workflow_type == FelicaStandard) {
15+
furi_string_cat_printf(
16+
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
1017
}
1118
}
1219

@@ -32,6 +39,11 @@ void nfc_render_felica_info(
3239
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
3340
}
3441

42+
FuriString* ic_type_str = furi_string_alloc();
43+
felica_get_ic_name(data, ic_type_str);
44+
furi_string_cat_printf(str, "IC Type:\n%s\n", furi_string_get_cstr(ic_type_str));
45+
furi_string_free(ic_type_str);
46+
3547
nfc_render_felica_idm(data, format_type, str);
3648

3749
if(format_type == NfcProtocolFormatTypeFull) {
@@ -40,6 +52,14 @@ void nfc_render_felica_info(
4052
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
4153
}
4254
}
55+
56+
furi_string_cat_printf(str, "\n");
57+
furi_string_cat_printf(
58+
str,
59+
"Services found: %lu \nAreas found: %lu\n",
60+
simple_array_get_count(data->services),
61+
simple_array_get_count(data->areas));
62+
4363
nfc_render_felica_blocks_count(data, str, true);
4464
}
4565

@@ -59,13 +79,18 @@ static void nfc_render_felica_block_name(
5979

6080
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
6181
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
62-
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
63-
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
64-
furi_string_cat_printf(str, "%02X ", block->data[j]);
82+
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
83+
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
6584
}
6685
furi_string_cat_printf(str, "\n");
6786
}
6887

88+
static void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {
89+
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
90+
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
91+
}
92+
}
93+
6994
static void nfc_render_felica_block(
7095
const FelicaBlock* block,
7196
FuriString* str,
@@ -76,8 +101,13 @@ static void nfc_render_felica_block(
76101
nfc_render_felica_block_data(block, str);
77102
}
78103

79-
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
104+
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {
80105
FuriString* name = furi_string_alloc();
106+
107+
furi_string_cat_printf(str, "\e#Blocks read:\n");
108+
109+
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
110+
81111
for(size_t i = 0; i < 14; i++) {
82112
furi_string_printf(name, "S_PAD%d", i);
83113
uint8_t suf_cnt = 18;
@@ -105,3 +135,70 @@ void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
105135
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
106136
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
107137
}
138+
139+
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
140+
const size_t area_count = simple_array_get_count(data->areas);
141+
const size_t service_count = simple_array_get_count(data->services);
142+
143+
furi_string_cat_printf(str, "\e#Directory Tree:\n");
144+
145+
if(area_count == 0 || service_count == 0) {
146+
furi_string_cat_printf(str, "No services or areas found.\n");
147+
} else {
148+
furi_string_cat_printf(
149+
str, "%zu areas found.\n%zu services found.\n\n", area_count, service_count);
150+
furi_string_cat_printf(
151+
str, "::: ... are readable services\n||| ... are locked services\n");
152+
}
153+
felica_write_directory_tree(data, str);
154+
}
155+
156+
void nfc_more_info_render_felica_blocks(
157+
const FelicaData* data,
158+
FuriString* str,
159+
const uint16_t service_code_key) {
160+
furi_string_cat_printf(str, "\n");
161+
if(data->workflow_type == FelicaLite) {
162+
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
163+
FuriString* name = furi_string_alloc();
164+
165+
for(size_t i = 0; i < 14; i++) {
166+
furi_string_printf(name, "S_PAD%d", i);
167+
uint8_t suf_cnt = 18;
168+
if(i == 1) {
169+
suf_cnt = 19;
170+
} else if((i == 10) || (i == 12) || (i == 13)) {
171+
suf_cnt = 16;
172+
}
173+
nfc_render_felica_block(
174+
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
175+
}
176+
furi_string_free(name);
177+
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
178+
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
179+
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
180+
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
181+
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
182+
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
183+
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
184+
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
185+
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
186+
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
187+
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
188+
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
189+
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
190+
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
191+
192+
} else if(data->workflow_type == FelicaStandard) {
193+
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
194+
for(size_t i = 0; i < public_blocks_count; i++) {
195+
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
196+
if(public_block->service_code != service_code_key) {
197+
continue; // Skip blocks not matching the requested service code
198+
}
199+
furi_string_cat_printf(str, "-----Block 0x%02X-----\n", public_block->block_idx);
200+
nfc_render_felica_block_data_simple(&public_block->block, str);
201+
furi_string_cat_printf(str, "\n");
202+
}
203+
}
204+
}

applications/main/nfc/helpers/protocol_support/felica/felica_render.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@ void nfc_render_felica_info(
1414
NfcProtocolFormatType format_type,
1515
FuriString* str);
1616

17-
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
17+
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);
1818

1919
void nfc_render_felica_idm(
2020
const FelicaData* data,
2121
NfcProtocolFormatType format_type,
2222
FuriString* str);
23+
24+
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);
25+
26+
void nfc_more_info_render_felica_blocks(
27+
const FelicaData* data,
28+
FuriString* str,
29+
const uint16_t service_code_key);

applications/main/nfc/scenes/nfc_scene_config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,6 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput)
7474
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
7575
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
7676

77+
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
78+
7779
ADD_SCENE(nfc, generate_info, GenerateInfo)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#include "../nfc_app_i.h"
2+
3+
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
4+
#include "../helpers/protocol_support/felica/felica_render.h"
5+
6+
enum {
7+
FelicaMoreInfoStateMenu,
8+
FelicaMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index
9+
};
10+
11+
enum SubmenuIndex {
12+
SubmenuIndexDirectory,
13+
SubmenuIndexDynamic, // dynamic indices start here
14+
};
15+
16+
void nfc_scene_felica_more_info_on_enter(void* context) {
17+
NfcApp* nfc = context;
18+
Submenu* submenu = nfc->submenu;
19+
20+
const uint32_t state =
21+
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
22+
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
23+
24+
submenu_add_item(
25+
submenu,
26+
"Directory",
27+
SubmenuIndexDirectory,
28+
nfc_protocol_support_common_submenu_callback,
29+
nfc);
30+
31+
FuriString* label = furi_string_alloc();
32+
33+
switch(data->workflow_type) {
34+
case FelicaLite:
35+
furi_string_printf(label, "All blocks");
36+
submenu_add_item(
37+
submenu,
38+
furi_string_get_cstr(label),
39+
SubmenuIndexDynamic,
40+
nfc_protocol_support_common_submenu_callback,
41+
nfc);
42+
break;
43+
case FelicaStandard:
44+
for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) {
45+
const FelicaService* service = simple_array_cget(data->services, i);
46+
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
47+
if(!is_public) {
48+
continue;
49+
}
50+
furi_string_printf(label, "Readable serv %04X", service->code);
51+
submenu_add_item(
52+
submenu,
53+
furi_string_get_cstr(label),
54+
i + SubmenuIndexDynamic,
55+
nfc_protocol_support_common_submenu_callback,
56+
nfc);
57+
}
58+
break;
59+
default:
60+
break;
61+
}
62+
63+
furi_string_free(label);
64+
65+
if(state >= FelicaMoreInfoStateItem) {
66+
submenu_set_selected_item(
67+
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
68+
scene_manager_set_scene_state(
69+
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
70+
}
71+
72+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
73+
}
74+
75+
bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) {
76+
NfcApp* nfc = context;
77+
bool consumed = false;
78+
79+
const uint32_t state =
80+
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
81+
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
82+
83+
if(event.type == SceneManagerEventTypeCustom) {
84+
if(event.event == SubmenuIndexDirectory) {
85+
FuriString* temp_str = furi_string_alloc();
86+
nfc_more_info_render_felica_dir(data, temp_str);
87+
88+
widget_add_text_scroll_element(
89+
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
90+
furi_string_free(temp_str);
91+
92+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
93+
scene_manager_set_scene_state(
94+
nfc->scene_manager,
95+
NfcSceneFelicaMoreInfo,
96+
FelicaMoreInfoStateItem + SubmenuIndexDirectory);
97+
consumed = true;
98+
} else {
99+
const uint16_t service_ind = event.event - 1; // offset the three enums above
100+
101+
text_box_reset(nfc->text_box);
102+
furi_string_reset(nfc->text_box_store);
103+
104+
switch(data->workflow_type) {
105+
case FelicaLite:
106+
nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store);
107+
break;
108+
case FelicaStandard:
109+
const FelicaService* service = simple_array_cget(data->services, service_ind);
110+
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
111+
nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code);
112+
break;
113+
default:
114+
furi_string_set_str(nfc->text_box_store, "IC type not implemented yet");
115+
break;
116+
}
117+
text_box_set_font(nfc->text_box, TextBoxFontHex);
118+
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
119+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
120+
scene_manager_set_scene_state(
121+
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event);
122+
consumed = true;
123+
}
124+
} else if(event.type == SceneManagerEventTypeBack) {
125+
if(state >= FelicaMoreInfoStateItem) {
126+
widget_reset(nfc->widget);
127+
text_box_reset(nfc->text_box);
128+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
129+
scene_manager_set_scene_state(
130+
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
131+
} else {
132+
widget_reset(nfc->widget);
133+
text_box_reset(nfc->text_box);
134+
// Return directly to the Info scene
135+
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);
136+
}
137+
consumed = true;
138+
}
139+
140+
return consumed;
141+
}
142+
143+
void nfc_scene_felica_more_info_on_exit(void* context) {
144+
NfcApp* nfc = context;
145+
146+
// Clear views
147+
widget_reset(nfc->widget);
148+
text_box_reset(nfc->text_box);
149+
furi_string_reset(nfc->text_box_store);
150+
submenu_reset(nfc->submenu);
151+
}

0 commit comments

Comments
 (0)