diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index 57eefbf67..69ac25013 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -40,10 +40,23 @@ #include +ARRAY_DEF(FelicaAreaPath, FelicaArea*, M_PTR_OPLIST); +ARRAY_DEF(FuriStringStack, FuriString*, M_PTR_OPLIST); ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST); #define NFC_TEXT_STORE_SIZE 128 + +typedef struct { + FuriStringStack_t strings; + + FelicaSystem* selected_system; + + FelicaAreaPath_t selected_areas; + + FelicaService* selected_service; +} FelicaSelectState; + typedef enum { NfcRpcStateIdle, NfcRpcStateEmulating, @@ -66,6 +79,7 @@ struct Nfc { FuriString* text_box_store; uint8_t byte_input_store[6]; MfClassicUserKeys_t mfc_key_strs; // Used in MFC key listing + FelicaSelectState felica_select; void* rpc_ctx; NfcRpcState rpc_state; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 07386bf22..a89d6c40b 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -46,6 +46,9 @@ ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, felica_read_success, FelicaReadSuccess) +ADD_SCENE(nfc, felica_menu, FelicaMenu) +ADD_SCENE(nfc, felica_info_select, FelicaInfoSelect) +ADD_SCENE(nfc, felica_service_data, FelicaServiceData) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) ADD_SCENE(nfc, device_info, DeviceInfo) ADD_SCENE(nfc, delete, Delete) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index cbb52bfd0..e20dbfd88 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -39,6 +39,8 @@ void nfc_scene_delete_on_enter(void* context) { furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_set(temp_str, "MIFARE DESFire"); + } else if(protocol == NfcDeviceProtocolFelica) { + furi_string_set(temp_str, "FeliCa"); } else { furi_string_set(temp_str, "Unknown ISO tag"); } diff --git a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c new file mode 100644 index 000000000..9f068822e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c @@ -0,0 +1,124 @@ +#include "../nfc_i.h" +#include + +#define SYSTEM_EVENT + +void nfc_scene_felica_info_select_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_felica_info_select_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + FelicaData* data = &nfc->dev->dev_data.felica_data; + FelicaSelectState* state = &nfc->felica_select; + + FuriStringStack_init(nfc->felica_select.strings); + FelicaAreaPath_init(nfc->felica_select.selected_areas); + + submenu_add_item(submenu, "[Actions]", 0, nfc_scene_felica_info_select_submenu_callback, nfc); + uint8_t i = 1; + if(state->selected_system == NULL) { + FelicaSystemList_it_t it; + for(FelicaSystemList_it(it, data->systems); !FelicaSystemList_end_p(it); FelicaSystemList_next(it)) { + FelicaSystem* current_system = *FelicaSystemList_ref(it); + FuriString* system_name = felica_get_system_name(current_system); + submenu_add_item( + submenu, + furi_string_get_cstr(system_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + FuriStringStack_push_back(state->strings, system_name); + } + } else if(FelicaAreaPath_size(state->selected_areas) == 0) { + FelicaArea* area_0 = &state->selected_system->root_area; + FelicaNodeList_it_t it; + for(FelicaNodeList_it(it, area_0->nodes); !FelicaNodeList_end_p(it); FelicaNodeList_next(it)) { + FelicaNode* node = *FelicaNodeList_ref(it); + if(node->type == FelicaNodeTypeArea) { + FuriString* area_name = furi_string_alloc_printf("Area %d", node->ptr.area->number); + submenu_add_item( + submenu, furi_string_get_cstr(area_name), i++, nfc_scene_felica_info_select_submenu_callback, nfc); + FuriStringStack_push_back(state->strings, area_name); + } else { + uint16_t service_code = node->ptr.service->number << 6; + FuriString* service_name = furi_string_alloc_printf("Service %04X", service_code); + submenu_add_item( + submenu, furi_string_get_cstr(service_name), i++, nfc_scene_felica_info_select_submenu_callback, nfc); + FuriStringStack_push_back(state->strings, service_name); + } + } + } + + state->selected_service = NULL; + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaInfoSelect)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + FelicaData* data = &nfc->dev->dev_data.felica_data; + FelicaSelectState* state = &nfc->felica_select; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + uint8_t index = event.event; + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, index); + + if(index == 0) { + return false; + } + + index -= 1; + if(state->selected_system == NULL) { + state->selected_system = *FelicaSystemList_get(data->systems, index); + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaInfoSelect); + consumed = true; + } else { + FelicaNode* selected_node = NULL; + if (FelicaAreaPath_size(state->selected_areas) == 0) { + selected_node = *FelicaNodeList_get(state->selected_system->root_area.nodes, index); + } else { + FelicaArea* current_area = *FelicaAreaPath_back(state->selected_areas); + selected_node = *FelicaNodeList_get(current_area->nodes, index); + } + + if(selected_node->type == FelicaNodeTypeArea) { + FelicaAreaPath_push_back(state->selected_areas, selected_node->ptr.area); + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaInfoSelect); + consumed = true; + } else if(selected_node->type == FelicaNodeTypeService) { + state->selected_service = selected_node->ptr.service; + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaServiceData); + consumed = true; + } + } + } else if(event.type == SceneManagerEventTypeBack) { + if(FelicaAreaPath_size(state->selected_areas) <= 1) { + FelicaAreaPath_clear(state->selected_areas); + state->selected_system = NULL; + } else { + FelicaAreaPath_pop_back(NULL, state->selected_areas); + } + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_felica_info_select_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + FelicaAreaPath_clear(nfc->felica_select.selected_areas); + FuriStringStack_it_t it; + for(FuriStringStack_it(it, nfc->felica_select.strings); !FuriStringStack_end_p(it); FuriStringStack_next(it)) { + furi_string_free(*FuriStringStack_ref(it)); + } + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_felica_menu.c b/applications/main/nfc/scenes/nfc_scene_felica_menu.c new file mode 100644 index 000000000..669bfd4ee --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_felica_menu.c @@ -0,0 +1,84 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + /* + SubmenuIndexUnlock, + SubmenuIndexSave, + SubmenuIndexEmulate, + */ + SubmenuIndexInfo, +}; + +void nfc_scene_felica_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_felica_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + // FelicaData* data = &nfc->dev->dev_data.felica_data; + + /* + submenu_add_item( + submenu, "Unlock", SubmenuIndexUnlock, nfc_scene_felica_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Save", SubmenuIndexSave, nfc_scene_felica_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_felica_menu_submenu_callback, nfc); + */ + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_scene_felica_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + /* + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatFelica; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } else if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockMenu); + consumed = true; + } else + */ + if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaInfoSelect); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_felica_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c index 80548c09d..81777a0f7 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c @@ -20,6 +20,8 @@ void nfc_scene_felica_read_success_on_enter(void* context) { Widget* widget = nfc->widget; widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_scene_felica_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_felica_read_success_widget_callback, nfc); FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { @@ -27,8 +29,9 @@ void nfc_scene_felica_read_success_on_enter(void* context) { } else { temp_str = furi_string_alloc_printf("\e#%s", nfc_felica_type(felica_data->type)); - FelicaSystem* current_system = felica_data->systems; - while(current_system) { + FelicaSystemList_it_t it; + for(FelicaSystemList_it(it, felica_data->systems); !FelicaSystemList_end_p(it); FelicaSystemList_next(it)) { + FelicaSystem* current_system = *FelicaSystemList_ref(it); furi_string_cat_printf( temp_str, "\nSystem %04X (#%d):", current_system->code, current_system->number); furi_string_cat_printf(temp_str, "\nIDm:\n "); @@ -39,8 +42,6 @@ void nfc_scene_felica_read_success_on_enter(void* context) { for(size_t i = 0; i < 8; i++) { furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); } - - current_system = current_system->next; } } @@ -61,7 +62,7 @@ bool nfc_scene_felica_read_success_on_event(void* context, SceneManagerEvent eve scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); consumed = true; } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaMenu); + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaMenu); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { diff --git a/applications/main/nfc/scenes/nfc_scene_felica_service_data.c b/applications/main/nfc/scenes/nfc_scene_felica_service_data.c new file mode 100644 index 000000000..7cb2ba591 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_felica_service_data.c @@ -0,0 +1,143 @@ +#include "../nfc_i.h" + +void nfc_scene_felica_service_data_on_enter(void* context) { + Nfc* nfc = context; + FelicaSelectState* select_state = &nfc->felica_select; + FelicaSystem* system = select_state->selected_system; + FelicaService* service = select_state->selected_service; + TextBox* text_box = nfc->text_box; + + if(system->code == LITE_SYSTEM_CODE && service->number == 0) { + text_box_set_font(text_box, TextBoxFontHex); + furi_string_cat_str(nfc->text_box_store, "S_PAD:\n"); + for(int i = 0; i < REG_LITE_BLOCK; i++) { + FelicaBlock* block = *FelicaBlockList_cget(service->blocks, i); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(block != NULL) { + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + } + } + + furi_string_cat_str(nfc->text_box_store, "REG:\n"); + FelicaBlock* block = *FelicaBlockList_cget(service->blocks, REG_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(block != NULL) { + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + } + + furi_string_cat_str(nfc->text_box_store, "RC:\n"); + block = *FelicaBlockList_cget(service->blocks, RC_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "MAC:\n"); + block = *FelicaBlockList_cget(service->blocks, MAC_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "ID:\n"); + block = *FelicaBlockList_cget(service->blocks, ID_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "D_ID:\n"); + block = *FelicaBlockList_cget(service->blocks, DEVICE_ID_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "CKV:\n"); + block = *FelicaBlockList_cget(service->blocks, CARD_KEY_VER_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "MC:\n"); + block = *FelicaBlockList_cget(service->blocks, MEM_CONFIG_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "WCNT:\n"); + block = *FelicaBlockList_cget(service->blocks, WRITE_COUNT_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "MAC_A:\n"); + block = *FelicaBlockList_cget(service->blocks, MAC_A_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + + furi_string_cat_str(nfc->text_box_store, "CRC_CHECK:\n"); + block = *FelicaBlockList_cget(service->blocks, CRC_CHECK_LITE_BLOCK); + for(uint16_t i = 0; i < FELICA_BLOCK_SIZE; i += 2) { + if(!(i % 8) && i) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + furi_string_cat_printf(nfc->text_box_store, "%02X%02X ", block->data[i], block->data[i + 1]); + } + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); +} + +bool nfc_scene_felica_service_data_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_felica_service_data_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); +} \ No newline at end of file diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a9999e20e..4f8c67f0a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,8.2,, +Version,+,8.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -792,8 +792,20 @@ Function,-,fdimf,float,"float, float" Function,-,fdiml,long double,"long double, long double" Function,-,fdopen,FILE*,"int, const char*" Function,-,felica_check_ic_type,_Bool,uint8_t* +Function,-,felica_clear,void,FelicaData* +Function,-,felica_define_normal_block,void,"FelicaService*, uint16_t, uint8_t*" Function,-,felica_get_ic_type,FelicaICType,uint8_t* +Function,-,felica_get_service_name,FuriString*,FelicaService* +Function,-,felica_get_system_name,FuriString*,FelicaSystem* +Function,-,felica_lite_can_read_without_mac,_Bool,"uint8_t*, uint8_t" +Function,-,felica_lite_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, _Bool, const uint8_t*, uint8_t" +Function,-,felica_lite_prepare_unencrypted_write,uint8_t,"uint8_t*, const FelicaReader*, const uint8_t*, uint8_t, const uint8_t*" +Function,-,felica_parse_unencrypted_read,uint16_t,"uint8_t*, uint8_t, FelicaReader*, uint8_t*, uint16_t" +Function,-,felica_parse_unencrypted_write,_Bool,"uint8_t*, uint8_t, FelicaReader*" +Function,-,felica_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, const uint16_t*, uint8_t, const uint32_t*, uint8_t" +Function,-,felica_prepare_unencrypted_write,uint8_t,"uint8_t*, FelicaReader*, const uint16_t*, uint8_t, const uint32_t*, uint8_t, const uint8_t*" Function,-,felica_read_card,_Bool,"FuriHalNfcTxRxContext*, FelicaData*, uint8_t*, uint8_t*" +Function,-,felica_read_lite_system,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" Function,-,feof,int,FILE* Function,-,feof_unlocked,int,FILE* Function,-,ferror,int,FILE* diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 32dae3c15..eb911f54b 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1221,6 +1221,8 @@ void nfc_device_data_clear(NfcDeviceData* dev_data) { mf_ul_reset(&dev_data->mf_ul_data); } else if(dev_data->protocol == NfcDeviceProtocolEMV) { memset(&dev_data->emv_data, 0, sizeof(EmvData)); + } else if(dev_data->protocol == NfcDeviceProtocolFelica) { + felica_clear(&dev_data->felica_data); } memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); dev_data->protocol = NfcDeviceProtocolUnknown; diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 54c696ff9..9065b73b5 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index 5cfa42d19..8387674df 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -68,6 +68,87 @@ bool felica_check_ic_type(uint8_t* PMm) { return true; } +FelicaICType felica_get_ic_type(uint8_t* PMm) { + uint8_t rom_type = PMm[0]; + uint8_t ic_type = PMm[1]; + + UNUSED(rom_type); + switch(ic_type) { + case 0xff: + return FelicaICTypeLink; + case 0xf2: + return FelicaICTypeLink; + case 0xf1: + return FelicaICTypeLiteS; + case 0xf0: + return FelicaICTypeLite; + case 0xe1: + return FelicaICTypeLink; + case 0xe0: + return FelicaICTypePlug; + case 0x48: + return FelicaICTypeSD2_6K; + case 0x47: + return FelicaICTypeRC_SA24_6K; + case 0x46: + return FelicaICTypeSD2_4K; + case 0x45: + case 0x44: + return FelicaICTypeSD2WithDES; + case 0x3e: + return FelicaICTypeRC_SA08; + case 0x35: + return FelicaICTypeSD1; + case 0x32: + return FelicaICTypeSD1WithDES; + case 0x31: + return FelicaICTypeSuica; + case 0x20: + return FelicaICTypeFRAM_4K; + case 0x1f: + case 0x1e: + case 0x1d: + case 0x1c: + case 0x1b: + case 0x1a: + case 0x19: + case 0x18: + return FelicaICTypeMobileIC_V4_1; + case 0x17: + return FelicaICTypeMobileIC_V4; + case 0x16: + case 0x15: + case 0x14: + return FelicaICTypeMobileIC_V3; + case 0x13: + case 0x12: + case 0x11: + case 0x10: + return FelicaICTypeMobileIC_V2; + case 0x0d: + return FelicaICTypeFRAM_9K; + case 0x0c: + return FelicaICTypeEMV_36K; + case 0x0b: // Old Suica? + return FelicaICTypeSuica; + case 0x09: + return FelicaICTypeEMV_16K; + case 0x08: + return FelicaICTypeEMV_32K; + case 0x07: + case 0x06: + return FelicaICTypeMobileIC_V1; + case 0x02: + return FelicaICType576B; + case 0x01: + return FelicaICType4K; + case 0x00: + return FelicaICType2K; + } + + return FelicaICType2K; +} + uint8_t felica_prepare_unencrypted_read( uint8_t* dest, const FelicaReader* reader, @@ -262,87 +343,6 @@ bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* rea return true; } -FelicaICType felica_get_ic_type(uint8_t* PMm) { - uint8_t rom_type = PMm[0]; - uint8_t ic_type = PMm[1]; - - UNUSED(rom_type); - switch(ic_type) { - case 0xff: - return FelicaICTypeLink; - case 0xf2: - return FelicaICTypeLink; - case 0xf1: - return FelicaICTypeLiteS; - case 0xf0: - return FelicaICTypeLite; - case 0xe1: - return FelicaICTypeLink; - case 0xe0: - return FelicaICTypePlug; - case 0x48: - return FelicaICTypeSD2_6K; - case 0x47: - return FelicaICTypeRC_SA24_6K; - case 0x46: - return FelicaICTypeSD2_4K; - case 0x45: - case 0x44: - return FelicaICTypeSD2WithDES; - case 0x3e: - return FelicaICTypeRC_SA08; - case 0x35: - return FelicaICTypeSD1; - case 0x32: - return FelicaICTypeSD1WithDES; - case 0x31: - return FelicaICTypeSuica; - case 0x20: - return FelicaICTypeFRAM_4K; - case 0x1f: - case 0x1e: - case 0x1d: - case 0x1c: - case 0x1b: - case 0x1a: - case 0x19: - case 0x18: - return FelicaICTypeMobileIC_V4_1; - case 0x17: - return FelicaICTypeMobileIC_V4; - case 0x16: - case 0x15: - case 0x14: - return FelicaICTypeMobileIC_V3; - case 0x13: - case 0x12: - case 0x11: - case 0x10: - return FelicaICTypeMobileIC_V2; - case 0x0d: - return FelicaICTypeFRAM_9K; - case 0x0c: - return FelicaICTypeEMV_36K; - case 0x0b: // Old Suica? - return FelicaICTypeSuica; - case 0x09: - return FelicaICTypeEMV_16K; - case 0x08: - return FelicaICTypeEMV_32K; - case 0x07: - case 0x06: - return FelicaICTypeMobileIC_V1; - case 0x02: - return FelicaICType576B; - case 0x01: - return FelicaICType4K; - case 0x00: - return FelicaICType2K; - } - - return FelicaICType2K; -} - void felica_parse_system_info(FelicaSystem* system, uint8_t* IDm, uint8_t* PMm) { memcpy(system->idm, IDm, 8); memcpy(system->pmm, PMm, 8); @@ -369,7 +369,7 @@ void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t FelicaBlock* block = malloc(sizeof(FelicaBlock)); block->type = FelicaBlockTypeNormal; memcpy(block->data, data, FELICA_BLOCK_SIZE); - service->blocks[number] = block; + FelicaBlockList_set_at(service->blocks, number, block); } bool felica_read_lite_system( @@ -407,15 +407,23 @@ bool felica_read_lite_system( } system->code = LITE_SYSTEM_CODE; + FelicaArea* area = &system->root_area; FelicaService* service = malloc(sizeof(FelicaService)); - system->services = service; - service->number = 0; - service->block_count = CRC_CHECK_LITE_BLOCK; - service->blocks = malloc(sizeof(FelicaBlock*) * service->block_count); - for(int i = 0; i < service->block_count; i++) { - service->blocks[i] = NULL; + FelicaBlockList_init(service->blocks); + for(int i = 0; i < CRC_CHECK_LITE_BLOCK; i++) { + FelicaBlockList_push_back(service->blocks, NULL); } + area->number = 0; + area->end_service_code = 0x000f; + FelicaNodeList_init(area->nodes); + FelicaNode* node = malloc(sizeof(node)); + node->type = FelicaNodeTypeService, + node->ptr.service = service; + FelicaNodeList_push_back(area->nodes, node); + + service->number = 0; + felica_define_normal_block(service, SYS_CODE_LITE_BLOCK, block_data); memset(block_data, 0, FELICA_BLOCK_SIZE); @@ -549,10 +557,10 @@ bool felica_read_card( memcpy(reader.current_pmm, polled_pmm, 8); FelicaSystem* current_system = malloc(sizeof(FelicaSystem)); - data->systems = current_system; + FelicaSystemList_init(data->systems); + FelicaSystemList_push_back(data->systems, current_system); felica_parse_system_info(current_system, polled_idm, polled_pmm); - current_system->next = NULL; if(data->type == FelicaICTypeLite || data->type == FelicaICTypeLiteS) { FURI_LOG_I(TAG, "Reading Felica Lite system"); @@ -564,3 +572,35 @@ bool felica_read_card( return card_read; } + +void felica_service_clear(FelicaService* service) { + FelicaBlockList_it_t it; + for(FelicaBlockList_it(it, service->blocks); !FelicaBlockList_end_p(it); FelicaBlockList_next(it)) { + FelicaBlock* block = *FelicaBlockList_ref(it); + free(block); + } + FelicaBlockList_clear(service->blocks); +} + +void felica_area_clear(FelicaArea* area) { + FelicaNodeList_it_t it; + for(FelicaNodeList_it(it, area->nodes); !FelicaNodeList_end_p(it); FelicaNodeList_next(it)) { + FelicaNode* node = *FelicaNodeList_ref(it); + if (node->type == FelicaNodeTypeArea) { + felica_area_clear(node->ptr.area); + } else if(node->type == FelicaNodeTypeService) { + felica_service_clear(node->ptr.service); + } + free(node); + } + FelicaNodeList_clear(area->nodes); +} + +void felica_clear(FelicaData* data) { + FelicaSystemList_it_t it; + for(FelicaSystemList_it(it, data->systems); !FelicaSystemList_end_p(it); FelicaSystemList_next(it)) { + FelicaSystem* system = *FelicaSystemList_ref(it); + felica_area_clear(&system->root_area); + } + FelicaSystemList_clear(data->systems); +} \ No newline at end of file diff --git a/lib/nfc/protocols/felica.h b/lib/nfc/protocols/felica.h index 1f0912802..f79d28ce6 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -1,6 +1,7 @@ #pragma once #include +#include #define NFCF_F_SIG (13560000.0) #define MRT_T_SIG 302064.89 //ns, 256 * 16 / NFC_F_SIG * 1e9 @@ -113,12 +114,6 @@ typedef enum { } FelicaMRTCommandType; typedef FelicaMRTParts FelicaMRTParameters[6]; - -typedef struct { - uint16_t number; - uint16_t end_service_code; -} FelicaArea; - typedef enum { FelicaBlockTypeNormal, FelicaBlockTypeOverlap, @@ -134,14 +129,37 @@ typedef struct { // typedef struct {} FelicaOverlapBlock; -typedef struct _FelicaService_t { - uint16_t number; - uint16_t block_count; - FelicaBlock** blocks; +ARRAY_DEF(FelicaBlockList, FelicaBlock*, M_PTR_OPLIST); - struct _FelicaService_t* next; +typedef struct { + uint16_t number; + FelicaBlockList_t blocks; } FelicaService; +typedef enum { + FelicaNodeTypeArea, + FelicaNodeTypeService, +} FelicaNodeType; + +struct _FelicaArea_t; +typedef struct { + FelicaNodeType type; + union { + struct _FelicaArea_t* area; + FelicaService* service; + } ptr; +} FelicaNode; +ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST); + +typedef struct _FelicaArea_t { + uint16_t number; + bool can_create_subareas; + uint16_t end_service_code; + + FelicaNodeList_t nodes; + +} FelicaArea; + typedef struct _FelicaSystem_t { uint8_t number; uint16_t code; @@ -149,16 +167,18 @@ typedef struct _FelicaSystem_t { uint8_t pmm[8]; FelicaMRTParameters maximum_response_times; - FelicaService* services; - - struct _FelicaSystem_t* next; + /** This struct represents area 0, + * which always exists on a given system + */ + FelicaArea root_area; } FelicaSystem; +ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST); + typedef struct { FelicaICType type; uint8_t subtype; - uint8_t system_count; - FelicaSystem* systems; + FelicaSystemList_t systems; } FelicaData; typedef struct { @@ -170,8 +190,56 @@ typedef struct { bool felica_check_ic_type(uint8_t* PMm); FelicaICType felica_get_ic_type(uint8_t* PMm); + +uint8_t felica_prepare_unencrypted_read( + uint8_t* dest, + const FelicaReader* reader, + const uint16_t* service_code_list, + uint8_t service_count, + const uint32_t* block_list, + uint8_t block_count); +uint8_t felica_lite_prepare_unencrypted_read( + uint8_t* dest, + const FelicaReader* reader, + bool is_read_only, + const uint8_t* block_list, + uint8_t block_count); +uint16_t felica_parse_unencrypted_read( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + uint8_t* out, + uint16_t out_len); + +uint8_t felica_prepare_unencrypted_write( + uint8_t* dest, + FelicaReader* reader, + const uint16_t* service_code_list, + uint8_t service_count, + const uint32_t* block_list, + uint8_t block_count, + const uint8_t* block_data); +uint8_t felica_lite_prepare_unencrypted_write( + uint8_t* dest, + const FelicaReader* reader, + const uint8_t* block_list, + uint8_t block_count, + const uint8_t* block_data); +bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader); + +bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number); + +void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t* data); + +bool felica_read_lite_system( + FuriHalNfcTxRxContext* tx_rx, + FelicaReader* reader, + FelicaData* data, + FelicaSystem* system); + bool felica_read_card( FuriHalNfcTxRxContext* tx_rx, FelicaData* data, uint8_t* polled_idm, - uint8_t* polled_pmm); \ No newline at end of file + uint8_t* polled_pmm); +void felica_clear(FelicaData* data); \ No newline at end of file diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c new file mode 100644 index 000000000..a5198aa09 --- /dev/null +++ b/lib/nfc/protocols/felica_util.c @@ -0,0 +1,43 @@ +#include "./felica.h" +#include + +FuriString* felica_get_system_name(FelicaSystem* system) { + uint16_t code = system->code; + + const char* prefix; + if(code == SUICA_SYSTEM_CODE) { + prefix = "SuiCa"; + } else if(code == NDEF_SYSTEM_CODE) { + prefix = "NDEF"; + } else if(code == HCE_F_SYSTEM_CODE) { + prefix = "HCE-F"; + } else if(code == OCTOPUS_SYSTEM_CODE) { + prefix = "Octopus"; + } else if(code == EDY_SYSTEM_CODE) { + prefix = "Edy"; + } else if(code == PASPY_SYSTEM_CODE) { + prefix = "PASPY"; + } else if(code == BLACKBOARD_SYSTEM_CODE) { + prefix = "Blackboard"; + } else if(code == SAPICA_SYSTEM_CODE) { + prefix = "SAPICA"; + } else if(code == LITE_SYSTEM_CODE) { + prefix = "FeliCa Lite"; + } else if(code == RYUTO_SYSTEM_CODE) { + prefix = "Ryuto"; + } else if(code == OKICA_SYSTEM_CODE) { + prefix = "OKICA"; + } else if(code == SECURE_ID_SYSTEM_CODE) { + prefix = "FeliCa Secure ID"; + } else if(code == IRUCA_SYSTEM_CODE) { + prefix = "IruCa"; + } else if(code == COMMON_AREA_SYSTEM_CODE) { + prefix = "Common Area"; + } else if(code == PLUG_SYSTEM_CODE) { + prefix = "FeliCa Plug"; + } else { + return furi_string_alloc_printf("System %04X", code); + } + + return furi_string_alloc_printf("%s (%04X)", prefix, code); +} \ No newline at end of file diff --git a/lib/nfc/protocols/felica_util.h b/lib/nfc/protocols/felica_util.h new file mode 100644 index 000000000..4224668eb --- /dev/null +++ b/lib/nfc/protocols/felica_util.h @@ -0,0 +1,4 @@ +#include "./felica.h" + +FuriString* felica_get_system_name(FelicaSystem* system); +FuriString* felica_get_service_name(FelicaService* service); \ No newline at end of file