diff --git a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c index 3b7c570d7..7a59521fa 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c @@ -19,24 +19,24 @@ void nfc_scene_felica_info_select_on_enter(void* context) { uint8_t i = 1; if(state->selected_system == NULL || state->selected_system->code == LITE_SYSTEM_CODE) { submenu_set_header(submenu, "Systems"); - 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); - furi_string_free(system_name); - } + for + M_EACH(current_system, data->systems, FelicaSystemArray_t) { + 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); + furi_string_free(system_name); + } } else { FelicaSystem* system = state->selected_system; FuriString* header = furi_string_alloc_printf("%04X/", system->code); - FelicaArea* area = &system->root_area; + FelicaNode* root = &system->root; + furi_assert(root->type == FelicaNodeTypeArea); + FelicaArea* area = root->area; if(FelicaAreaPath_size(state->selected_areas) > 0) { FelicaAreaPath_it_t it; for(FelicaAreaPath_it(it, state->selected_areas); !FelicaAreaPath_end_p(it); @@ -51,32 +51,30 @@ void nfc_scene_felica_info_select_on_enter(void* context) { submenu_set_header(submenu, furi_string_get_cstr(header)); furi_string_free(header); - FelicaNodeList_it_t it; - for(FelicaNodeList_it(it, area->nodes); !FelicaNodeList_end_p(it); - FelicaNodeList_next(it)) { - FelicaNode* node = *FelicaNodeList_ref(it); - FuriString* node_name = furi_string_alloc(); - if(node->type == FelicaNodeTypeArea) { - furi_string_printf(node_name, "Area %d", node->area->number); - submenu_add_item( - submenu, - furi_string_get_cstr(node_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - } else { - uint16_t service_code = node->service->number << 6; - furi_string_printf(node_name, "Service %04X", service_code); - submenu_add_item( - submenu, - furi_string_get_cstr(node_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - } + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + FuriString* node_name = furi_string_alloc(); + if(node->type == FelicaNodeTypeArea) { + furi_string_printf(node_name, "Area %d", node->area->number); + submenu_add_item( + submenu, + furi_string_get_cstr(node_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + } else { + uint16_t service_code = node->service->number << 6; + furi_string_printf(node_name, "Service %04X", service_code); + submenu_add_item( + submenu, + furi_string_get_cstr(node_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + } - furi_string_free(node_name); - } + furi_string_free(node_name); + } } state->selected_service = NULL; @@ -102,7 +100,7 @@ bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent even index -= 1; if(state->selected_system == NULL) { - state->selected_system = *FelicaSystemList_get(data->systems, index); + state->selected_system = FelicaSystemArray_get(data->systems, index); if(state->selected_system->code == LITE_SYSTEM_CODE) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaServiceData); } else { @@ -111,12 +109,15 @@ bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent even consumed = true; } else { FelicaNode* selected_node = NULL; + + FelicaNode* root = &(state->selected_system->root); + furi_assert(root->type == FelicaNodeTypeArea); + if(FelicaAreaPath_size(state->selected_areas) == 0) { - selected_node = - *FelicaNodeList_get(state->selected_system->root_area.nodes, index); + selected_node = FelicaNodeArray_get(root->area->nodes, index); } else { FelicaArea* current_area = *FelicaAreaPath_back(state->selected_areas); - selected_node = *FelicaNodeList_get(current_area->nodes, index); + selected_node = FelicaNodeArray_get(current_area->nodes, index); } if(selected_node->type == FelicaNodeTypeArea) { 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 52bba0ee3..79dd82297 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c @@ -29,21 +29,19 @@ 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)); - 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 "); - for(size_t i = 0; i < 8; i++) { - furi_string_cat_printf(temp_str, "%02X", current_system->idm[i]); + for + M_EACH(current_system, felica_data->systems, FelicaSystemArray_t) { + furi_string_cat_printf( + temp_str, "\nSystem %04X (#%d):", current_system->code, current_system->number); + furi_string_cat_printf(temp_str, "\nIDm:\n "); + for(size_t i = 0; i < 8; i++) { + furi_string_cat_printf(temp_str, "%02X", current_system->idm[i]); + } + furi_string_cat_printf(temp_str, "\nPMm:\n "); + for(size_t i = 0; i < 8; i++) { + furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); + } } - furi_string_cat_printf(temp_str, "\nPMm:\n "); - for(size_t i = 0; i < 8; i++) { - furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); - } - } } widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 8f8391a64..26f9c4eb3 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -66,35 +66,35 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { nfc_data->f_data.pmm[0], nfc_data->f_data.pmm[1]); - furi_string_cat_printf(temp_str, "Timings (1 node/blk):\n"); + furi_string_cat_printf(temp_str, "MRT (1 node/blk):\n"); furi_string_cat_printf( temp_str, "- ReqSvc: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[2], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_VARIABLE_MRT], 1)); furi_string_cat_printf( temp_str, "- Fixed: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[3], 0)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_FIXED_MRT], 0)); furi_string_cat_printf( temp_str, "- Auth1: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[4], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_MUTUAL_AUTH_MRT], 1)); furi_string_cat_printf( temp_str, "- Auth2: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[4], 0)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_MUTUAL_AUTH_MRT], 0)); furi_string_cat_printf( temp_str, "- Read: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[5], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_READ_MRT], 1)); furi_string_cat_printf( temp_str, "- Write: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[6], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_WRITE_MRT], 1)); furi_string_cat_printf( temp_str, "- Other: %" PRIuLEAST32 "us\n\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[7], 0)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_OTHER_MRT], 0)); furi_string_cat_printf(temp_str, "IDm:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c index 865b3f54b..ed33515d4 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_type.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -8,6 +8,7 @@ enum SubmenuIndex { SubmenuIndexReadEMV, SubmenuIndexReadNFCA, SubmenuIndexReadFelica, + SubmenuIndexReadNFCF, }; void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) { @@ -56,6 +57,12 @@ void nfc_scene_read_card_type_on_enter(void* context) { SubmenuIndexReadFelica, nfc_scene_read_card_type_submenu_callback, nfc); + submenu_add_item( + submenu, + "Read NFC-F data", + SubmenuIndexReadNFCF, + nfc_scene_read_card_type_submenu_callback, + nfc); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType); submenu_set_selected_item(submenu, state); @@ -97,6 +104,11 @@ bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } + if(event.event == SubmenuIndexReadNFCF) { + nfc->dev->dev_data.read_mode = NfcReadModeNFCF; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event); } return consumed; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 935f0eca8..fb5603041 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,+,11.13,, +Version,+,11.14,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -808,14 +808,15 @@ 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_dump_data,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" 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_push_normal_block,void,"FelicaService*, 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.h b/lib/nfc/nfc_device.h index fc0e5bfbc..3edd01070 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -63,6 +63,7 @@ typedef enum { NfcReadModeEMV, NfcReadModeNFCA, NfcReadModeFelica, + NfcReadModeNFCF, } NfcReadMode; typedef struct { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index b034dd202..96cab9f5b 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -508,6 +508,10 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { break; } } + } else if(read_mode == NfcReadModeNFCF) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + event = NfcWorkerEventReadUidNfcF; + break; } } } else { diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index b3405cf6b..827187564 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -151,6 +151,55 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } +/** Parse common FeliCa response headers. + * + * This parses and validates the most commonly occurring response header types. + * + * The header needs to match the (length, res, idm) format, and also (sf1, sf2) when always_succeed + * is set to false. + * + * @param buf RX buffer. + * @param len RX buffer length. + * @param reader The FeliCa reader context. + * @param expected_resp Expected response code. Must be an odd number. + * @param always_succeed When set to true, skip status flags (sf1 and sf2) parsing. + * @return The number of bytes parsed, or 0 when response is invalid or status flags are set. + */ +static uint8_t felica_consume_header( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + uint8_t expected_resp, + bool always_succeed) { + furi_assert(expected_resp & 1); + furi_assert(buf != NULL); + furi_assert(reader != NULL); + + uint8_t header_size = always_succeed ? 10 : 12; + if(len < header_size) { + FURI_LOG_E(TAG, "Malformed header: too short."); + return 0; + } + if(buf[1] != expected_resp) { + FURI_LOG_E(TAG, "Expecting %u, got %u.", expected_resp, buf[1]); + return 0; + } + if(memcmp(&buf[2], reader->current_idm, 8) != 0) { + FURI_LOG_E(TAG, "IDm mismatch."); + return 0; + } + if(always_succeed) { + reader->status_flags[0] = buf[10]; + reader->status_flags[1] = buf[11]; + if(reader->status_flags[0] != 0 || reader->status_flags[1] != 0) { + FURI_LOG_W( + TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); + return 0; + } + } + return header_size; +} + uint8_t felica_prepare_unencrypted_read( uint8_t* dest, const FelicaReader* reader, @@ -212,32 +261,12 @@ uint16_t felica_parse_unencrypted_read( FelicaReader* reader, uint8_t* out, uint16_t out_len) { - if(len < 12) { - return false; - } - len--; - buf++; - - if(*buf != FELICA_UNENCRYPTED_READ_RES) { - return false; - } - len--; - buf++; - - if(memcmp(buf, reader->current_idm, 8) != 0) { - return false; - } - len -= 8; - buf += 8; - - reader->status_flags[0] = buf[0]; - reader->status_flags[1] = buf[1]; - len -= 2; - buf += 2; - if(reader->status_flags[0] != 0) { - FURI_LOG_W(TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); + uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); + if(!consumed) { return 0; } + len -= consumed; + buf += consumed; if(len < 1) { return 0; @@ -315,48 +344,77 @@ uint8_t felica_lite_prepare_unencrypted_write( } bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) { - if(len < 12) { + uint8_t consumed = + felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); + if(!consumed) { return false; } + return true; +} + +uint8_t felica_prepare_request_system_code(uint8_t* dest, FelicaReader* reader) { + dest[0] = FELICA_REQUEST_SYSTEM_CODE_CMD; + memcpy(&dest[1], reader->current_idm, 8); + return 9; +} + +bool felica_parse_request_system_code( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + FelicaSystemArray_t* systems) { + uint8_t consumed = + felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); + if(consumed == 0) { + return false; + } + len -= consumed; + buf += consumed; + + uint8_t entries = *buf; len--; buf++; - if(*buf != FELICA_UNENCRYPTED_WRITE_RES) { + if(len < 2 * entries) { + FURI_LOG_E(TAG, "FELICA_REQUEST_SYSTEM_CODE_RES: Response too short"); return false; } - len--; - buf++; - if(memcmp(buf, reader->current_idm, 8) != 0) { - return false; - } - len -= 8; - buf += 8; + for(uint8_t idx = 0; idx < entries; idx++) { + FelicaSystem* system = FelicaSystemArray_push_new(*systems); + furi_assert(system != NULL); - reader->status_flags[0] = buf[0]; - reader->status_flags[1] = buf[1]; - len -= 2; - buf += 2; - if(reader->status_flags[0] != 0) { - FURI_LOG_W(TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); - return 0; + // Set system code + system->number = idx; + system->code = buf[2 * idx] | (buf[2 * idx + 1] << 8); + + FURI_LOG_D(TAG, "Found system code %04X", system->code); + + // Fill in IDm and PMm + memcpy(system->idm, reader->current_idm, 8); + memcpy(system->pmm, reader->current_pmm, 8); + + // Set system index field in IDm + system->idm[0] &= 0x0f; + system->idm[0] |= ((idx & 0xf) << 4); } return true; } -void felica_parse_system_info(FelicaSystem* system, uint8_t* IDm, uint8_t* PMm) { - memcpy(system->idm, IDm, 8); - memcpy(system->pmm, PMm, 8); - for(int i = 0; i < 6; i++) { - char MRT_byte = PMm[2 + i]; - FelicaMRTParts* mrt_data = &system->maximum_response_times[i]; - mrt_data->real_a = (MRT_byte & 7) + 1; - MRT_byte >>= 3; - mrt_data->real_b = (MRT_byte & 7) + 1; - MRT_byte >>= 3; - mrt_data->exponent = (MRT_byte & 3); - } +static FelicaSystem* felica_gen_monolithic_system_code( + FelicaReader* reader, + FelicaSystemArray_t* systems, + uint16_t system_code) { + FelicaSystem* system = FelicaSystemArray_push_new(*systems); + furi_assert(reader != NULL); + furi_assert(system != NULL); + + memcpy(system->idm, reader->current_idm, 8); + memcpy(system->pmm, reader->current_pmm, 8); + system->code = system_code; + + return system; } bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number) { @@ -368,12 +426,16 @@ 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) { - FelicaBlock* block = malloc(sizeof(FelicaBlock)); + FelicaBlock* block = FelicaBlockArray_safe_get(service->blocks, number); memcpy(block->data, data, FELICA_BLOCK_SIZE); - FelicaBlockList_set_at(service->blocks, number, block); } -bool felica_read_lite_system( +void felica_push_normal_block(FelicaService* service, uint8_t* data) { + FelicaBlock* block = FelicaBlockArray_push_new(service->blocks); + memcpy(block->data, data, FELICA_BLOCK_SIZE); +} + +bool felica_lite_dump_data( FuriHalNfcTxRxContext* tx_rx, FelicaReader* reader, FelicaData* data, @@ -425,8 +487,6 @@ bool felica_read_lite_system( return false; } - system->code = LITE_SYSTEM_CODE; - FelicaLiteInfo* lite_info = &system->lite_info; lite_info->card_key_1 = NULL; lite_info->card_key_2 = NULL; @@ -568,6 +628,22 @@ bool felica_read_lite_system( return true; } +bool felica_std_request_system_code( + FuriHalNfcTxRxContext* tx_rx, + FelicaReader* reader, + FelicaSystemArray_t* systems) { + tx_rx->tx_bits = 8 * felica_prepare_request_system_code(tx_rx->tx_data, reader); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_E(TAG, "Bad exchange requesting system code"); + return false; + } + if(!felica_parse_request_system_code(tx_rx->rx_data, tx_rx->rx_bits / 8, reader, systems)) { + FURI_LOG_E(TAG, "Bad response to Request System Code command"); + return false; + } + return true; +} + bool felica_read_card( FuriHalNfcTxRxContext* tx_rx, FelicaData* data, @@ -583,31 +659,24 @@ bool felica_read_card( memcpy(reader.current_idm, polled_idm, 8); memcpy(reader.current_pmm, polled_pmm, 8); - FelicaSystem* current_system = malloc(sizeof(FelicaSystem)); - FelicaSystemList_init(data->systems); - FelicaSystemList_push_back(data->systems, current_system); - - felica_parse_system_info(current_system, polled_idm, polled_pmm); + FelicaSystemArray_init(data->systems); if(data->type == FelicaICTypeLite || data->type == FelicaICTypeLiteS) { FURI_LOG_I(TAG, "Reading Felica Lite system"); - felica_read_lite_system(tx_rx, &reader, data, current_system); + FelicaSystem* lite_system = + felica_gen_monolithic_system_code(&reader, &(data->systems), LITE_SYSTEM_CODE); + felica_lite_dump_data(tx_rx, &reader, data, lite_system); card_read = true; break; } + FURI_LOG_I(TAG, "Reading Felica Standard system"); } while(false); 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); + FelicaBlockArray_clear(service->blocks); } void felica_lite_clear(FelicaLiteInfo* lite_info) { @@ -630,31 +699,33 @@ void felica_lite_clear(FelicaLiteInfo* lite_info) { } } +void felica_node_clear(FelicaNode* node); + 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->area); - } else if(node->type == FelicaNodeTypeService) { - felica_service_clear(node->service); + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + felica_node_clear(node); } - free(node); + FelicaNodeArray_clear(area->nodes); +} + +void felica_node_clear(FelicaNode* node) { + if(node->type == FelicaNodeTypeArea) { + felica_area_clear(node->area); + } else if(node->type == FelicaNodeTypeService) { + felica_service_clear(node->service); } - 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); - if(system->code == LITE_SYSTEM_CODE) { - felica_lite_clear(&system->lite_info); - ; - } else { - felica_area_clear(&system->root_area); + for + M_EACH(system, data->systems, FelicaSystemArray_t) { + if(system->code == LITE_SYSTEM_CODE) { + felica_lite_clear(&system->lite_info); + } else { + felica_node_clear(&system->root); + FelicaPublicServiceDict_clear(system->public_services); + } } - } - FelicaSystemList_clear(data->systems); + FelicaSystemArray_clear(data->systems); } \ No newline at end of file diff --git a/lib/nfc/protocols/felica.h b/lib/nfc/protocols/felica.h index fa81cba2f..21bd5532c 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -10,6 +10,8 @@ #define MRT_T_SIG_x16 4833038.24 //ns, MRT_T_SIG * (4 ** 2) #define MRT_T_SIG_x64 19332152.96 //ns, MRT_T_SIG * (4 ** 2) +#define FELICA_PMM_MRT_BASE 2 + #define FELICA_VARIABLE_MRT 0 #define FELICA_FIXED_MRT 1 #define FELICA_MUTUAL_AUTH_MRT 2 @@ -17,6 +19,13 @@ #define FELICA_WRITE_MRT 4 #define FELICA_OTHER_MRT 5 +#define FELICA_PMM_VARIABLE_MRT (FELICA_PMM_MRT_BASE + FELICA_VARIABLE_MRT) +#define FELICA_PMM_FIXED_MRT (FELICA_PMM_MRT_BASE + FELICA_FIXED_MRT) +#define FELICA_PMM_MUTUAL_AUTH_MRT (FELICA_PMM_MRT_BASE + FELICA_MUTUAL_AUTH_MRT) +#define FELICA_PMM_READ_MRT (FELICA_PMM_MRT_BASE + FELICA_READ_MRT) +#define FELICA_PMM_WRITE_MRT (FELICA_PMM_MRT_BASE + FELICA_WRITE_MRT) +#define FELICA_PMM_OTHER_MRT (FELICA_PMM_MRT_BASE + FELICA_OTHER_MRT) + #define FELICA_BLOCK_SIZE 16 #define CYBERNET_SYSTEM_CODE 0x0003 @@ -55,9 +64,13 @@ #define FELICA_UNENCRYPTED_READ_CMD 0x06 #define FELICA_UNENCRYPTED_WRITE_CMD 0x08 +#define FELICA_SEARCH_SERVICE_CODE_CMD 0x0a +#define FELICA_REQUEST_SYSTEM_CODE_CMD 0x0c #define FELICA_UNENCRYPTED_READ_RES 0x07 #define FELICA_UNENCRYPTED_WRITE_RES 0x09 +#define FELICA_SEARCH_SERVICE_CODE_RES 0x0b +#define FELICA_REQUEST_SYSTEM_CODE_RES 0x0d typedef enum { FelicaICTypeRC_SA24_10K, // RC-SA24/1x @@ -88,13 +101,6 @@ typedef enum { FelicaICTypeSuica, // https://www.tuv-nederland.nl/assets/files/cerfiticaten/2019/07/cr-nscib-cc-10-30076-cr.pdf } FelicaICType; -typedef struct { - uint8_t exponent : 2; - // Incremented at read - uint8_t real_a : 4; - uint8_t real_b : 4; -} FelicaMRTParts; - typedef enum { FelicaServiceTypeRandom = (0b0010 << 2), FelicaServiceTypeCyclic = (0b0011 << 2), @@ -121,20 +127,22 @@ DICT_SET_DEF( FelicaServiceAttribute, M_ENUM_OPLIST(FelicaServiceAttribute, FelicaServiceAttributeAuthRW)) -typedef FelicaMRTParts FelicaMRTParameters[6]; - typedef struct { uint8_t data[FELICA_BLOCK_SIZE]; } FelicaBlock; -ARRAY_DEF(FelicaBlockList, FelicaBlock*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaBlockList, FelicaBlock*, M_PTR_OPLIST) +ARRAY_DEF(FelicaBlockArray, FelicaBlock, M_POD_OPLIST) +#define M_OPL_FelicaBlockArray_t() ARRAY_OPLIST(FelicaBlockArray, M_POD_OPLIST) typedef struct { uint16_t number; FelicaServiceAttributeList_t access_control_list; // accounts for overlap services - bool is_extended_overlap; + bool is_extended_overlap; // We don't know much about this currently. will always be false union { - FelicaBlockList_t blocks; + // TODO change this to use FelicaBlockArray_t + FelicaBlockArray_t blocks; struct { uint16_t overlap_target; uint8_t block_start; @@ -149,23 +157,44 @@ typedef enum { } FelicaNodeType; struct _FelicaArea_t; -typedef struct { +typedef struct _FelicaArea_t FelicaArea; + +struct _FelicaNode_s; +typedef struct _FelicaNode_s FelicaNode; + +struct _FelicaNode_s { + /** Node type. */ FelicaNodeType type; + /** Borrowed pointer to its parent node. */ + FelicaNode* parent; union { - struct _FelicaArea_t* area; + /** (Area/dir type only) The area struct. */ + FelicaArea* area; + /** (Service/file type only) The service struct. */ FelicaService* service; }; -} FelicaNode; +}; -ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST) +ARRAY_DEF(FelicaNodeArray, FelicaNode, M_POD_OPLIST) +#define M_OPL_FelicaNodeArray_t() ARRAY_OPLIST(FelicaNodeArray, M_POD_OPLIST) -typedef struct _FelicaArea_t { +ARRAY_DEF(FelicaNodeRefArray, FelicaNode*, M_PTR_OPLIST) +#define M_OPL_FelicaNodeRefArray_t() ARRAY_OPLIST(FelicaNodeRefArray, M_PTR_OPLIST) + +// { service_number: service_ptr_in_tree } +DICT_DEF2(FelicaPublicServiceDict, uint16_t, M_DEFAULT_OPLIST, FelicaService*, M_PTR_OPLIST) +#define M_OPL_FelicaPublicServiceDict_t() \ + DICT_OPLIST(FelicaPublicServiceDict, M_DEFAULT_OPLIST, M_PTR_OPLIST) + +struct _FelicaArea_t { uint16_t number; bool can_create_subareas; uint16_t end_service_code; - FelicaNodeList_t nodes; -} FelicaArea; + FelicaNodeArray_t nodes; +}; typedef struct { uint8_t* S_PAD[14]; @@ -186,24 +215,39 @@ typedef struct { } FelicaLiteInfo; typedef struct _FelicaSystem_t { + /** FeliCa system index. */ uint8_t number; + /** If the system belongs to a FeliCa Lite (and be its only system). */ + bool is_lite; + /** FeliCa system code. */ uint16_t code; + /** System IDm with system index bitfield properly set. */ uint8_t idm[8]; + /** Cached card PMm. */ uint8_t pmm[8]; - FelicaMRTParameters maximum_response_times; union { + /** (For FeliCa Lite only) Card content. */ FelicaLiteInfo lite_info; - FelicaArea root_area; + struct { + /** (For FeliCa Standard only) The root of the raw filesystem tree. */ + FelicaNode root; + /** (For FeliCa Standard only) Shortcut for all publicly accessible services for quick + * access by card parsers. */ + FelicaPublicServiceDict_t public_services; + }; }; } FelicaSystem; -ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST) +ARRAY_DEF(FelicaSystemArray, FelicaSystem, M_POD_OPLIST) +#define M_OPL_FelicaSystemArray_t() ARRAY_OPLIST(FelicaSystemArray, M_POD_OPLIST) typedef struct { FelicaICType type; uint8_t subtype; - FelicaSystemList_t systems; + FelicaSystemArray_t systems; } FelicaData; typedef struct { @@ -255,8 +299,17 @@ bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* rea 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); +void felica_push_normal_block(FelicaService* service, uint8_t* data); -bool felica_read_lite_system( +/** Dump a FeliCa Lite or Lite-S tag. + * + * @param tx_rx NFC context. + * @param reader FeliCa reader context. + * @param data Output data object. + * @param system FeliCa system description. + * @return true if successful. + */ +bool felica_lite_dump_data( FuriHalNfcTxRxContext* tx_rx, FelicaReader* reader, FelicaData* data, diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index aba13c4be..481ea5ed7 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -3,6 +3,7 @@ static const uint32_t TIME_CONSTANT_US = 302; +// TODO move this to felica.c uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units) { uint_least32_t base_cost_factor = 1 + (timing & 0x7); uint_least32_t unit_cost_factor = 1 + ((timing >> 3) & 0x7);