diff --git a/applications/main/nfc/scenes/nfc_scene_felica_menu.c b/applications/main/nfc/scenes/nfc_scene_felica_menu.c index b989047d6..7aaba30db 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_menu.c @@ -4,7 +4,9 @@ enum SubmenuIndex { /* SubmenuIndexUnlock, + */ SubmenuIndexSave, + /* SubmenuIndexEmulate, */ SubmenuIndexInfo, @@ -24,8 +26,10 @@ void nfc_scene_felica_menu_on_enter(void* context) { /* 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); */ @@ -43,13 +47,13 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { 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)) { @@ -61,9 +65,8 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockMenu); consumed = true; - } else */ - if(event.event == SubmenuIndexInfo) { + } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaInfoSelect); consumed = true; } diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index 06b8133fa..a9e29b431 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 06b8133fa295474507b55b1a5695d4b8bf804ed6 +Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 6a2bcfcc0..09d9008d6 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -20,6 +20,7 @@ static const uint32_t nfc_keys_file_version = 1; // Protocols format versions static const uint32_t nfc_mifare_classic_data_format_version = 2; static const uint32_t nfc_mifare_ultralight_data_format_version = 1; +static const uint32_t nfc_felica_data_format_version = 1; NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); @@ -58,6 +59,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatFelica) { + furi_string_set(format_string, "FeliCa"); } else { furi_string_set(format_string, "Unknown"); } @@ -781,6 +784,192 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* return saved; } +static bool nfc_device_save_felica_lite(FlipperFormat* file, FelicaLiteInfo* info) { + bool saved = false; + FuriString* key = furi_string_alloc(); + + do { + flipper_format_write_comment_cstr(file, "Lite(-S) System"); + flipper_format_write_hex(file, "Data Format Code", info->data_format_code, sizeof(uint16_t)); + flipper_format_write_hex(file, "ID Arbitrary Value", info->ID_value, 6); + flipper_format_write_hex(file, "Memory Config", info->memory_config, FELICA_BLOCK_SIZE); + + for(uint8_t block_num = 0; block_num < 14; block_num++) { + FuriString* spad_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->S_PAD[block_num] != NULL) { + furi_string_cat_printf(spad_str, "%02X ", info->S_PAD[block_num][i]); + } else { + furi_string_cat_printf(spad_str, "?? "); + } + } + + furi_string_printf(key, "S_PAD%d", block_num); + flipper_format_write_string(file, furi_string_get_cstr(key), spad_str); + } + + FuriString* reg_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(reg_str, "%02X ", info->REG[i]); + } else { + furi_string_cat_printf(reg_str, "?? "); + } + } + flipper_format_write_string(file, "REG", reg_str); + + flipper_format_write_hex(file, "Card Key Version", &info->card_key_version, sizeof(uint16_t)); + FuriString* ck1_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(ck1_str, "%02X ", info->card_key_1[i]); + } else { + furi_string_cat_printf(ck1_str, "?? "); + } + } + flipper_format_write_string(file, "Card Key 1", ck1_str); + + FuriString* ck2_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(ck2_str, "%02X ", info->card_key_2[i]); + } else { + furi_string_cat_printf(ck2_str, "?? "); + } + } + flipper_format_write_string(file, "Card Key 2", ck2_str); + + flipper_format_write_hex(file, "Fixed Challenge MAC Response", info->MAC, 8); + + flipper_format_write_bool(file, "Is Lite-S", &info->is_lite_s, 1); + if (info->is_lite_s) { + flipper_format_write_hex(file, "Fixed Challenge MAC-A Response", info->MAC_A, 8); + flipper_format_write_uint32(file, "Write Count", &info->write_count, 1); + } + + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_area(FlipperFormat* file, FelicaArea* area) { + bool saved = false; + FuriString* prefix = furi_string_alloc_printf("Area %d", area->number); + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Can Create Subareas", prefix); + flipper_format_write_bool(file, furi_string_get_cstr(key), &area->can_create_subareas, 1); + furi_string_printf(key, "%s End Service Code", prefix); + flipper_format_write_hex(file, furi_string_get_cstr(key), (uint8_t*)&area->end_service_code, sizeof(uint16_t)); + + bool node_saved = true; + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + if (nfc_device_save_felica_node(file, node)) { + node_saved = false; + break; + } + } + + if (!node_saved) break; + saved = true; + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_service(FlipperFormat* file, FelicaService* service) { + bool saved = false; + FuriString* prefix = furi_string_alloc_printf("Service %d", service->number); + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Is Extended Overlap", prefix); + flipper_format_write_bool(file, furi_string_get_cstr(key), &service->is_extended_overlap, 1); + if (service->is_extended_overlap) { + furi_string_printf(key, "%s Overlap Target", prefix); + flipper_format_write_hex(file, furi_string_get_cstr(key), (uint8_t*)&service->overlap_target, sizeof(uint16_t)); + + furi_string_printf(key, "%s Block Start", prefix); + const uint32_t block_start = service->block_start; + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_start, 1); + + furi_string_printf(key, "%s Block Count", prefix); + const uint32_t block_count = service->block_count; + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); + + uint32_t i = 0; + for + M_EACH(block, service->blocks, FelicaBlockArray_t) { + furi_string_printf(key, "%s Block %d", prefix, i); + flipper_format_write_hex(file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); + } + } else { + furi_string_printf(key, "%s Block Count", prefix); + uint32_t block_count = FelicaBlockArray_size(service->blocks); + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); + uint32_t i = 0; + for + M_EACH(block, service->blocks, FelicaBlockArray_t) { + furi_string_printf(key, "%s Block %d", prefix, i); + flipper_format_write_hex(file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); + } + } + + saved = true; + } while(false); + +} + +static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node) { + bool saved = false; + FuriString* key = furi_string_alloc(); + + do { + if(node->type == FelicaNodeTypeArea) { + if(!nfc_device_save_felica_node(file, node->area)) { + saved = false; + break; + } + } else if(node->type == FelicaNodeTypeService) { + if(!nfc_device_save_felica_service(file, node->area)) { + saved = false; + break; + } + } + + saved = true; + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + FelicaData* data = &dev->dev_data.felica_data; + // Save FeliCa specific data + do { + if(!flipper_format_write_comment_cstr(file, "FeliCa specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_felica_data_format_version, 1)) + break; + + for + M_EACH(system, data->systems, FelicaSystemArray_t) { + flipper_format_write_hex(file, "System", &system->number, sizeof(uint8_t)); + flipper_format_write_hex(file, "Code", (uint8_t*)&system->code, sizeof(uint16_t)); + if (system->code == LITE_SYSTEM_CODE) { + nfc_device_save_felica_lite(file, &system->lite_info); + } else { + nfc_device_save_felica_node(file, &system->root); + } + } + } while(false); + + return saved; +} + static void nfc_device_load_mifare_classic_block( FuriString* block_str, MfClassicData* data, @@ -1069,30 +1258,35 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, FeliCa")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; - if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; - // Save more data if necessary - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_save_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_save_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - // Save data - if(!nfc_device_save_mifare_classic_data(file, dev)) break; - // Save keys cache - if(!nfc_device_save_mifare_classic_keys(dev)) break; + if(data->type == FuriHalNfcTypeA) { + // Write UID, ATQA, SAK + if(!flipper_format_write_comment_cstr( + file, "UID, ATQA and SAK are common for all A formats")) + break; + if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; + // Save more data if necessary + if(dev->format == NfcDeviceSaveFormatMifareUl) { + if(!nfc_device_save_mifare_ul_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { + if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { + // Save data + if(!nfc_device_save_mifare_classic_data(file, dev)) break; + // Save keys cache + if(!nfc_device_save_mifare_classic_keys(dev)) break; + } + saved = true; + } else if(data->type == FuriHalNfcTypeF) { + if(!nfc_device_save_felica_data(file, dev)) break; } - saved = true; } while(0); if(!saved) { //-V547 diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index c5823a221..bf9da65f5 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -799,6 +799,7 @@ bool felica_lite_dump_data( } } if(data->type == FelicaICTypeLiteS) { + lite_info->is_lite_s = true; const uint8_t fixed_s_blocks[] = { CARD_KEY_LITE_BLOCK, MAC_A_LITE_BLOCK, diff --git a/lib/nfc/protocols/felica.h b/lib/nfc/protocols/felica.h index f8e504102..876596bb5 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -206,6 +206,7 @@ typedef struct { uint16_t card_key_version; uint8_t memory_config[FELICA_BLOCK_SIZE]; + bool is_lite_s; // Lite-S only uint8_t MAC_A[8]; uint32_t write_count; @@ -294,8 +295,6 @@ uint8_t felica_lite_prepare_unencrypted_write( 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); void felica_push_normal_block(FelicaService* service, uint8_t* data);