diff --git a/.gitmodules b/.gitmodules index 671e7e2c4..4fba0483e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,7 @@ [submodule "assets/protobuf"] path = assets/protobuf url = https://github.com/flipperdevices/flipperzero-protobuf.git + shallow = false [submodule "lib/libusb_stm32"] path = lib/libusb_stm32 url = https://github.com/flipperdevices/libusb_stm32.git diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index e01f56be1..caf740e9a 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -287,7 +287,8 @@ static void test_rpc_create_simple_message( PB_Main* message, uint16_t tag, const char* str, - uint32_t command_id) { + uint32_t command_id, + bool flag) { furi_check(message); char* str_copy = NULL; @@ -308,6 +309,7 @@ static void test_rpc_create_simple_message( break; case PB_Main_storage_list_request_tag: message->content.storage_list_request.path = str_copy; + message->content.storage_list_request.include_md5 = flag; break; case PB_Main_storage_mkdir_request_tag: message->content.storage_mkdir_request.path = str_copy; @@ -419,6 +421,7 @@ static void } mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); + mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum); if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? @@ -430,10 +433,10 @@ static void } static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { - mu_check(result->command_id == expected->command_id); - mu_check(result->command_status == expected->command_status); - mu_check(result->has_next == expected->has_next); - mu_check(result->which_content == expected->which_content); + mu_assert_int_eq(expected->command_id, result->command_id); + mu_assert_int_eq(expected->command_status, result->command_status); + mu_assert_int_eq(expected->has_next, result->has_next); + mu_assert_int_eq(expected->which_content, result->which_content); if(result->command_status != PB_CommandStatus_OK) { mu_check(result->which_content == PB_Main_empty_tag); } @@ -573,10 +576,15 @@ static void static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, - uint32_t command_id) { + uint32_t command_id, + bool append_md5) { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + PB_Main response = { .command_id = command_id, .has_next = false, @@ -614,6 +622,17 @@ static void test_rpc_storage_list_create_expected_list( list->file[i].data = NULL; /* memory free inside rpc_encode_and_send() -> pb_release() */ list->file[i].name = name; + + if(append_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf(md5_path, "%s/%s", path, name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } } else { @@ -626,6 +645,10 @@ static void test_rpc_storage_list_create_expected_list( response.has_next = false; MsgList_push_back(msg_list, response); + furi_string_free(md5); + furi_string_free(md5_path); + storage_file_free(file); + storage_dir_close(dir); storage_file_free(dir); @@ -675,16 +698,17 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) { MsgList_clear(msg_list); } -static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { +static void test_rpc_storage_list_run(const char* path, uint32_t command_id, bool md5) { PB_Main request; MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_list_request_tag, path, command_id, md5); if(!strcmp(path, "/")) { test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id); } else { - test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id, md5); } test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -694,15 +718,25 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_list) { - test_rpc_storage_list_run("/", ++command_id); - test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id); + test_rpc_storage_list_run("/", ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false); + test_rpc_storage_list_run("error_path", ++command_id, false); +} - test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id); - test_rpc_storage_list_run("error_path", ++command_id); +MU_TEST(test_storage_list_md5) { + test_rpc_storage_list_run("/", ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true); + test_rpc_storage_list_run("error_path", ++command_id, true); } static void @@ -770,7 +804,8 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { MsgList_init(expected_msg_list); test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id); - test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_read_request_tag, path, command_id, false); test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -824,7 +859,8 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_info_request_tag, path, command_id, false); PB_Main* response = MsgList_push_new(expected_msg_list); response->command_id = command_id; @@ -856,7 +892,8 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_stat_request_tag, path, command_id, false); Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; @@ -968,7 +1005,11 @@ static void test_storage_write_read_run( test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id); test_rpc_create_simple_message( - MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id); + MsgList_push_raw(input_msg_list), + PB_Main_storage_read_request_tag, + path, + ++*command_id, + false); test_rpc_add_read_or_write_to_list( expected_msg_list, READ_RESPONSE, @@ -1041,7 +1082,8 @@ MU_TEST(test_storage_interrupt_continuous_same_system) { MsgList_push_new(input_msg_list), PB_Main_storage_mkdir_request_tag, TEST_DIR "dir1", - command_id + 1); + command_id + 1, + false); test_rpc_add_read_or_write_to_list( input_msg_list, WRITE_REQUEST, @@ -1121,7 +1163,8 @@ static void test_storage_delete_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_delete_request_tag, path, command_id, false); request.content.storage_delete_request.recursive = recursive; test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1202,7 +1245,8 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_mkdir_request_tag, path, command_id, false); test_rpc_add_empty_to_list(expected_msg_list, status, command_id); test_rpc_encode_and_feed_one(&request, 0); @@ -1229,33 +1273,15 @@ MU_TEST(test_storage_mkdir) { static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t once_read_size = 512; - const uint8_t hash_size = MD5SUM_SIZE; - uint8_t* data = malloc(once_read_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, once_read_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } - - free(hash); - free(data); + if(md5_string_calc_file(file, path, md5, NULL)) { + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); } else { furi_check(0); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); @@ -1271,11 +1297,12 @@ static void test_storage_md5sum_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_md5sum_request_tag, path, command_id, false); if(status == PB_CommandStatus_OK) { PB_Main* response = MsgList_push_new(expected_msg_list); test_rpc_create_simple_message( - response, PB_Main_storage_md5sum_response_tag, md5sum, command_id); + response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false); response->command_status = status; } else { test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1433,6 +1460,7 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_info); MU_RUN_TEST(test_storage_stat); MU_RUN_TEST(test_storage_list); + MU_RUN_TEST(test_storage_list_md5); MU_RUN_TEST(test_storage_read); MU_RUN_TEST(test_storage_write_read); MU_RUN_TEST(test_storage_write); @@ -1731,7 +1759,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_0), PB_Main_storage_read_request_tag, TEST_DIR "file0.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id); @@ -1739,7 +1768,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_1), PB_Main_storage_read_request_tag, TEST_DIR "file1.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id); diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index f0b45c598..13188e5e0 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -582,6 +582,49 @@ MU_TEST(test_storage_common_migrate) { furi_record_close(RECORD_STORAGE); } +#define MD5_HASH_SIZE (16) +#include + +MU_TEST(test_md5_calc) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + const char* path = UNIT_TESTS_PATH("storage/md5.txt"); + const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2"; + const uint8_t md5[MD5_HASH_SIZE] = { + 0x2a, + 0x45, + 0x6f, + 0xa4, + 0x3e, + 0x75, + 0x08, + 0x8f, + 0xdd, + 0xe4, + 0x1c, + 0x93, + 0x15, + 0x9d, + 0x62, + 0xa2, + }; + + uint8_t md5_output[MD5_HASH_SIZE]; + FuriString* md5_output_str = furi_string_alloc(); + memset(md5_output, 0, MD5_HASH_SIZE); + + mu_check(md5_calc_file(file, path, md5_output, NULL)); + mu_check(md5_string_calc_file(file, path, md5_output_str, NULL)); + + mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE); + mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str)); + + storage_file_free(file); + furi_string_free(md5_output_str); + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(test_data_path) { MU_RUN_TEST(test_storage_data_path); MU_RUN_TEST(test_storage_data_path_apps); @@ -591,11 +634,16 @@ MU_TEST_SUITE(test_storage_common) { MU_RUN_TEST(test_storage_common_migrate); } +MU_TEST_SUITE(test_md5_calc_suite) { + MU_RUN_TEST(test_md5_calc); +} + int run_minunit_test_storage() { MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_dir); MU_RUN_SUITE(storage_rename); MU_RUN_SUITE(test_data_path); MU_RUN_SUITE(test_storage_common); + MU_RUN_SUITE(test_md5_calc_suite); return MU_EXIT_CODE; } diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 8debade2c..fe4228e47 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -7,7 +7,7 @@ App( requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"], stack_size=2 * 1024, order=20, - fap_version="2.3", + fap_version="3.20", fap_author="Alexander Kopachov (@akopachov)", fap_description="Software-based TOTP authenticator for Flipper Zero device", fap_weburl="https://github.com/akopachov/flipper-zero_authenticator", diff --git a/applications/external/totp/cli/cli.c b/applications/external/totp/cli/cli.c index c860b5a36..4cb68ce83 100644 --- a/applications/external/totp/cli/cli.c +++ b/applications/external/totp/cli/cli.c @@ -16,6 +16,10 @@ #include "commands/automation/automation.h" #include "commands/details/details.h" +struct TotpCliContext { + PluginState* plugin_state; +}; + static void totp_cli_print_unknown_command(const FuriString* unknown_command) { TOTP_CLI_PRINTF_ERROR( "Command \"%s\" is unknown. Use \"" TOTP_CLI_COMMAND_HELP @@ -63,7 +67,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) { totp_cli_command_automation_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { - totp_cli_command_reset_handle(plugin_state, cli, cli_context->event_queue); + totp_cli_command_reset_handle(plugin_state, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) { totp_cli_command_update_handle(plugin_state, args, cli); } else if( @@ -77,13 +81,11 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -TotpCliContext* - totp_cli_register_command_handler(PluginState* plugin_state, FuriMessageQueue* event_queue) { +TotpCliContext* totp_cli_register_command_handler(PluginState* plugin_state) { Cli* cli = furi_record_open(RECORD_CLI); TotpCliContext* context = malloc(sizeof(TotpCliContext)); furi_check(context != NULL); context->plugin_state = plugin_state; - context->event_queue = event_queue; cli_add_command( cli, TOTP_CLI_COMMAND_NAME, CliCommandFlagParallelSafe, totp_cli_handler, context); furi_record_close(RECORD_CLI); diff --git a/applications/external/totp/cli/cli.h b/applications/external/totp/cli/cli.h index 2e4b92db8..13afef78f 100644 --- a/applications/external/totp/cli/cli.h +++ b/applications/external/totp/cli/cli.h @@ -3,11 +3,17 @@ #include #include "../types/plugin_state.h" -typedef struct { - PluginState* plugin_state; - FuriMessageQueue* event_queue; -} TotpCliContext; +typedef struct TotpCliContext TotpCliContext; -TotpCliContext* - totp_cli_register_command_handler(PluginState* plugin_state, FuriMessageQueue* event_queue); +/** + * @brief Registers TOTP CLI handler + * @param plugin_state application state + * @return TOTP CLI context + */ +TotpCliContext* totp_cli_register_command_handler(PluginState* plugin_state); + +/** + * @brief Unregisters TOTP CLI handler + * @param context application state + */ void totp_cli_unregister_command_handler(TotpCliContext* context); \ No newline at end of file diff --git a/applications/external/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h index b8f4f236a..9c40bff13 100644 --- a/applications/external/totp/cli/cli_helpers.h +++ b/applications/external/totp/cli/cli_helpers.h @@ -33,12 +33,14 @@ extern const char* TOTP_CLI_COLOR_INFO; #define TOTP_CLI_PRINTF_INFO(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) -#define TOTP_CLI_LOCK_UI(plugin_state) \ - Scene __previous_scene = plugin_state->current_scene; \ - totp_scene_director_activate_scene(plugin_state, TotpSceneStandby) +#define TOTP_CLI_LOCK_UI(plugin_state) \ + Scene __previous_scene = plugin_state->current_scene; \ + totp_scene_director_activate_scene(plugin_state, TotpSceneStandby); \ + totp_scene_director_force_redraw(plugin_state) -#define TOTP_CLI_UNLOCK_UI(plugin_state) \ - totp_scene_director_activate_scene(plugin_state, __previous_scene) +#define TOTP_CLI_UNLOCK_UI(plugin_state) \ + totp_scene_director_activate_scene(plugin_state, __previous_scene); \ + totp_scene_director_force_redraw(plugin_state) /** * @brief Checks whether user is authenticated and entered correct PIN. @@ -109,6 +111,6 @@ void totp_cli_print_error_updating_config_file(); void totp_cli_print_error_loading_token_info(); /** - * @brief Prints message to let user knwo that command is processing now + * @brief Prints message to let user know that command is processing now */ void totp_cli_print_processing(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index fa64bd41c..d2bf8b907 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -11,7 +11,7 @@ struct TotpAddContext { FuriString* args; Cli* cli; - uint8_t* iv; + const CryptoSettings* crypto_settings; }; enum TotpIteratorUpdateTokenResultsEx { @@ -54,7 +54,7 @@ static TotpIteratorUpdateTokenResult // Reading token secret furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]:\r\n"); if(!totp_cli_read_line(context_t->cli, temp_str, mask_user_input)) { totp_cli_delete_last_line(); furi_string_secure_free(temp_str); @@ -68,7 +68,7 @@ static TotpIteratorUpdateTokenResult furi_string_get_cstr(temp_str), furi_string_size(temp_str), token_secret_encoding, - context_t->iv); + context_t->crypto_settings); furi_string_secure_free(temp_str); @@ -166,7 +166,8 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl TOTP_CLI_LOCK_UI(plugin_state); - struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + struct TotpAddContext add_context = { + .args = args, .cli = cli, .crypto_settings = &plugin_state->crypto_settings}; TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context); diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c index c9f6ac34b..87c121dbe 100644 --- a/applications/external/totp/cli/commands/automation/automation.c +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -7,17 +7,23 @@ #define TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD "automation" #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "none" #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "usb" -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "bt" #endif +#define TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY "QWERTY" +#define TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY "AZERTY" +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX "-k" +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT "layout" void totp_cli_command_automation_docopt_commands() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation method\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation settings\r\n"); } void totp_cli_command_automation_docopt_usage() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL( - DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL(DOCOPT_OPTION( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT))) " " DOCOPT_OPTIONAL(DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); } void totp_cli_command_automation_docopt_arguments() { @@ -25,24 +31,33 @@ void totp_cli_command_automation_docopt_arguments() { " " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD " Automation method to be set. Must be one of: " TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT #endif "\r\n"); } -static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) { -#ifdef TOTP_BADBT_TYPE_ENABLED +void totp_cli_command_automation_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT)) " Automation keyboard layout. Must be one of: " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY + ", " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY + "\r\n"); +} + +static void print_method(AutomationMethod method, const char* color) { +#ifdef TOTP_BADBT_AUTOMATION_ENABLED bool has_previous_method = false; #endif if(method & AutomationMethodBadUsb) { TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "\""); -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED has_previous_method = true; #endif } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(method & AutomationMethodBadBt) { if(has_previous_method) { TOTP_CLI_PRINTF_COLORFUL(color, " and "); @@ -57,6 +72,37 @@ static void totp_cli_command_automation_print_method(AutomationMethod method, co } } +static void print_kb_layout(AutomationKeyboardLayout layout, const char* color) { + char* layoutToPrint; + switch(layout) { + case AutomationKeyboardLayoutQWERTY: + layoutToPrint = TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY; + break; + case AutomationKeyboardLayoutAZERTY: + layoutToPrint = TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY; + break; + default: + furi_crash("Unknown automation keyboard layout"); + break; + } + + TOTP_CLI_PRINTF_COLORFUL(color, "%s", layoutToPrint); +} + +static bool + parse_automation_keyboard_layout(const FuriString* str, AutomationKeyboardLayout* out) { + bool result = true; + if(furi_string_cmpi_str(str, TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY) == 0) { + *out = AutomationKeyboardLayoutQWERTY; + } else if(furi_string_cmpi_str(str, TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY) == 0) { + *out = AutomationKeyboardLayoutAZERTY; + } else { + result = false; + } + + return result; +} + void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; @@ -65,6 +111,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a FuriString* temp_str = furi_string_alloc(); bool new_method_provided = false; AutomationMethod new_method = AutomationMethodNone; + AutomationKeyboardLayout new_kb_layout = plugin_state->automation_kb_layout; bool args_valid = true; while(args_read_string_and_trim(args, temp_str)) { if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE) == 0) { @@ -74,13 +121,19 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a new_method_provided = true; new_method |= AutomationMethodBadUsb; } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT) == 0) { new_method_provided = true; new_method |= AutomationMethodBadBt; } #endif - else { + else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX) == 0) { + if(!args_read_string_and_trim(args, temp_str) || + !parse_automation_keyboard_layout(temp_str, &new_kb_layout)) { + args_valid = false; + break; + } + } else { args_valid = false; break; } @@ -96,15 +149,19 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a TOTP_CLI_LOCK_UI(plugin_state); plugin_state->automation_method = new_method; + plugin_state->automation_kb_layout = new_kb_layout; if(totp_config_file_update_automation_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); - totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + TOTP_CLI_PRINTF_SUCCESS(" ("); + print_kb_layout(plugin_state->automation_kb_layout, TOTP_CLI_COLOR_SUCCESS); + TOTP_CLI_PRINTF_SUCCESS(")"); cli_nl(); } else { totp_cli_print_error_updating_config_file(); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(!(new_method & AutomationMethodBadBt) && plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); @@ -115,8 +172,10 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current automation method is "); - totp_cli_command_automation_print_method( - plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + print_method(plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + TOTP_CLI_PRINTF_INFO(" ("); + print_kb_layout(plugin_state->automation_kb_layout, TOTP_CLI_COLOR_INFO); + TOTP_CLI_PRINTF_INFO(")"); cli_nl(); } } while(false); diff --git a/applications/external/totp/cli/commands/automation/automation.h b/applications/external/totp/cli/commands/automation/automation.h index fb62e638e..4a713d49b 100644 --- a/applications/external/totp/cli/commands/automation/automation.h +++ b/applications/external/totp/cli/commands/automation/automation.h @@ -8,4 +8,5 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli); void totp_cli_command_automation_docopt_commands(); void totp_cli_command_automation_docopt_usage(); -void totp_cli_command_automation_docopt_arguments(); \ No newline at end of file +void totp_cli_command_automation_docopt_arguments(); +void totp_cli_command_automation_docopt_options(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/help/help.c b/applications/external/totp/cli/commands/help/help.c index cc47db11f..7093877ea 100644 --- a/applications/external/totp/cli/commands/help/help.c +++ b/applications/external/totp/cli/commands/help/help.c @@ -64,4 +64,6 @@ void totp_cli_command_help_handle() { totp_cli_command_add_docopt_options(); totp_cli_command_update_docopt_options(); totp_cli_command_delete_docopt_options(); + totp_cli_command_pin_docopt_options(); + totp_cli_command_automation_docopt_options(); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 62531b96a..2b989a713 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -7,19 +7,35 @@ #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include -#include "../../../services/crypto/crypto.h" +#include "../../../services/crypto/crypto_facade.h" #include "../../../ui/scene_director.h" #define TOTP_CLI_COMMAND_PIN_COMMAND_SET "set" #define TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE "remove" +#define TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX "-c" +#define TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT "slot" void totp_cli_command_pin_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_PIN " Set\\change\\remove PIN\r\n"); } void totp_cli_command_pin_docopt_usage() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_PIN " " DOCOPT_REQUIRED( - TOTP_CLI_COMMAND_PIN_COMMAND_SET " | " TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) "\r\n"); + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_PIN + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_PIN_COMMAND_SET " | " TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) " " DOCOPT_OPTIONAL( + DOCOPT_OPTION( + TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT))) "\r\n"); +} + +void totp_cli_command_pin_docopt_options() { + TOTP_CLI_PRINTF( + " " DOCOPT_OPTION( + TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT)) " New crypto key slot. Must be between %d and %d\r\n", + ACCEPTABLE_CRYPTO_KEY_SLOT_START, + ACCEPTABLE_CRYPTO_KEY_SLOT_END); } static inline uint8_t totp_cli_key_to_pin_code(uint8_t key) { @@ -89,35 +105,49 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl bool do_change = false; bool do_remove = false; - UNUSED(do_remove); - if(args_read_string_and_trim(args, temp_str)) { + uint8_t crypto_key_slot = plugin_state->crypto_settings.crypto_key_slot; + + bool arguments_parsed = true; + while(args_read_string_and_trim(args, temp_str)) { if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_SET) == 0) { do_change = true; } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) { do_remove = true; + } else if( + furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX) == + 0) { + if(!args_read_uint8_and_trim(args, &crypto_key_slot) || + !totp_crypto_check_key_slot(crypto_key_slot)) { + TOTP_CLI_PRINTF_ERROR("Slot \"%" PRIu8 "\" can not be used\r\n", crypto_key_slot); + arguments_parsed = false; + break; + } } else { totp_cli_print_invalid_arguments(); + arguments_parsed = false; + break; } - } else { - totp_cli_print_invalid_arguments(); } - if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { + if(!(do_change || do_remove) || (do_change && do_remove)) { + totp_cli_print_invalid_arguments(); + arguments_parsed = false; + } + + if(arguments_parsed && totp_cli_ensure_authenticated(plugin_state, cli)) { TOTP_CLI_LOCK_UI(plugin_state); do { - uint8_t old_iv[TOTP_IV_SIZE]; - memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); - uint8_t new_pin[TOTP_IV_SIZE]; - memset(&new_pin[0], 0, TOTP_IV_SIZE); + uint8_t new_pin[CRYPTO_IV_LENGTH]; + memset(&new_pin[0], 0, CRYPTO_IV_LENGTH); uint8_t new_pin_length = 0; if(do_change) { if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + memset_s(&new_pin[0], CRYPTO_IV_LENGTH, 0, CRYPTO_IV_LENGTH); break; } } else if(do_remove) { new_pin_length = 0; - memset(&new_pin[0], 0, TOTP_IV_SIZE); + memset(&new_pin[0], 0, CRYPTO_IV_LENGTH); } char* backup_path = totp_config_file_backup(plugin_state); @@ -127,7 +157,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl "Once you make sure everything is fine and works as expected, please delete this backup file\r\n"); free(backup_path); } else { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + memset_s(&new_pin[0], CRYPTO_IV_LENGTH, 0, CRYPTO_IV_LENGTH); TOTP_CLI_PRINTF_ERROR( "An error has occurred during taking backup of config file\r\n"); break; @@ -135,10 +165,10 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl TOTP_CLI_PRINTF("Encrypting...\r\n"); - bool update_result = - totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length); + bool update_result = totp_config_file_update_encryption( + plugin_state, crypto_key_slot, new_pin, new_pin_length); - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + memset_s(&new_pin[0], CRYPTO_IV_LENGTH, 0, CRYPTO_IV_LENGTH); totp_cli_delete_last_line(); diff --git a/applications/external/totp/cli/commands/pin/pin.h b/applications/external/totp/cli/commands/pin/pin.h index 1308ae736..2d320a02a 100644 --- a/applications/external/totp/cli/commands/pin/pin.h +++ b/applications/external/totp/cli/commands/pin/pin.h @@ -7,4 +7,5 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cli* cli); void totp_cli_command_pin_docopt_commands(); -void totp_cli_command_pin_docopt_usage(); \ No newline at end of file +void totp_cli_command_pin_docopt_usage(); +void totp_cli_command_pin_docopt_options(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index 96b2cc56a..250d594bf 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -17,10 +17,7 @@ void totp_cli_command_reset_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n"); } -void totp_cli_command_reset_handle( - PluginState* plugin_state, - Cli* cli, - FuriMessageQueue* event_queue) { +void totp_cli_command_reset_handle(PluginState* plugin_state, Cli* cli) { TOTP_CLI_LOCK_UI(plugin_state); TOTP_CLI_PRINTF_WARNING( "As a result of reset all the settings and tokens will be permanently lost.\r\n"); @@ -35,7 +32,7 @@ void totp_cli_command_reset_handle( totp_config_file_reset(plugin_state); TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n"); TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n"); - totp_cli_force_close_app(event_queue); + totp_cli_force_close_app(plugin_state->event_queue); } else { TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); TOTP_CLI_UNLOCK_UI(plugin_state); diff --git a/applications/external/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h index 3a4675587..221c84304 100644 --- a/applications/external/totp/cli/commands/reset/reset.h +++ b/applications/external/totp/cli/commands/reset/reset.h @@ -5,9 +5,6 @@ #define TOTP_CLI_COMMAND_RESET "reset" -void totp_cli_command_reset_handle( - PluginState* plugin_state, - Cli* cli, - FuriMessageQueue* event_queue); +void totp_cli_command_reset_handle(PluginState* plugin_state, Cli* cli); void totp_cli_command_reset_docopt_commands(); void totp_cli_command_reset_docopt_usage(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index 49beb7b6d..79ed75670 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -13,7 +13,7 @@ struct TotpUpdateContext { FuriString* args; Cli* cli; - uint8_t* iv; + const CryptoSettings* crypto_settings; }; enum TotpIteratorUpdateTokenResultsEx { @@ -83,7 +83,7 @@ static TotpIteratorUpdateTokenResult if(update_token_secret) { // Reading token secret furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]:\r\n"); bool token_secret_read = totp_cli_read_line(context_t->cli, temp_str, mask_user_input); totp_cli_delete_last_line(); if(!token_secret_read) { @@ -96,7 +96,7 @@ static TotpIteratorUpdateTokenResult furi_string_get_cstr(temp_str), furi_string_size(temp_str), token_secret_encoding, - context_t->iv)) { + context_t->crypto_settings)) { furi_string_secure_free(temp_str); return TotpIteratorUpdateTokenResultInvalidSecret; } @@ -151,7 +151,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, totp_token_info_iterator_go_to(iterator_context, token_number - 1); struct TotpUpdateContext update_context = { - .args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + .args = args, .cli = cli, .crypto_settings = &plugin_state->crypto_settings}; TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token( iterator_context, &update_token_handler, &update_context); diff --git a/applications/external/totp/cli/common_command_arguments.h b/applications/external/totp/cli/common_command_arguments.h index be1c0bdfe..0bf834225 100644 --- a/applications/external/totp/cli/common_command_arguments.h +++ b/applications/external/totp/cli/common_command_arguments.h @@ -18,32 +18,87 @@ #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX "-e" #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" +/** + * @brief Prints information about unknown argument + * @param arg + */ void totp_cli_printf_unknown_argument(const FuriString* arg); +/** + * @brief Prints information about missed required argument + * @param arg + */ void totp_cli_printf_missed_argument_value(char* arg); +/** + * @brief Tries to read token hashing algo + * @param token_info token info to set parsed algo to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token hashing algo sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token hashing algo argument; \c false otherwise + */ bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read token digits count + * @param token_info token info to set parsed digits count to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token digits count sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token digits count argument; \c false otherwise + */ bool totp_cli_try_read_digits( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read token duration + * @param token_info token info to set parsed duration to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token duration sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token duration argument; \c false otherwise + */ bool totp_cli_try_read_duration( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read token automation features + * @param token_info token info to set parsed automation features to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token automation features sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token automation features argument; \c false otherwise + */ bool totp_cli_try_read_automation_features( TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read unsecure flag + * @param arg argument to parse + * @param[out] parsed will be set to \c true if unsecure flag sucecssfully read and parsed; \c false otherwise + * @param[out] unsecure_flag will be set to parsed unsecure flag state if read and parsed successfully + * @return \c true if \c arg represents unsecure flag argument; \c false otherwise + */ bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); +/** + * @brief Tries to read plain token secret encoding + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if plain token secret encoding sucecssfully read and parsed; \c false otherwise + * @param[out] secret_encoding will be set to parsed plain token secret encoding if read and parsed successfully + * @return \c true if \c arg represents plain token secret encoding argument; \c false otherwise + */ bool totp_cli_try_read_plain_token_secret_encoding( FuriString* arg, FuriString* args, diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h index 54bbaba94..764f27bea 100644 --- a/applications/external/totp/features_config.h +++ b/applications/external/totp/features_config.h @@ -4,13 +4,8 @@ #endif // Include Bluetooth token input automation -#ifndef TOTP_NO_BADBT_TYPE -#define TOTP_BADBT_TYPE_ENABLED -#endif - -// Include token input automation icons on the main screen -#ifndef TOTP_NO_AUTOMATION_ICONS -#define TOTP_AUTOMATION_ICONS_ENABLED +#ifndef TOTP_NO_BADBT_AUTOMATION +#define TOTP_BADBT_AUTOMATION_ENABLED #endif // List of compatible firmwares @@ -19,6 +14,7 @@ #define TOTP_FIRMWARE_XTREME_UL (3) // End of list +// Target firmware #ifndef TOTP_TARGET_FIRMWARE #define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME_UL #endif diff --git a/applications/external/totp/images/totp_arrow_bottom_10x5.png b/applications/external/totp/images/totp_arrow_bottom_10x5.png deleted file mode 100644 index 54e22f5ef..000000000 Binary files a/applications/external/totp/images/totp_arrow_bottom_10x5.png and /dev/null differ diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 7e594a144..8d4320cf4 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -9,7 +9,8 @@ #include "../../types/common.h" #include "../../types/token_info.h" #include "../../features_config.h" -#include "../crypto/crypto.h" +#include "../crypto/crypto_facade.h" +#include "../crypto/constants.h" #include "migrations/common_migration.h" #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" @@ -110,7 +111,16 @@ static char* totp_config_file_backup_i(Storage* storage) { static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) { + bool conf_file_exists = storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK; + if(!conf_file_exists) { + FURI_LOG_I(LOGGING_TAG, "Application catalog needs to be migrated"); + FS_Error migration_result = + storage_common_migrate(storage, EXT_PATH("authenticator"), CONFIG_FILE_DIRECTORY_PATH); + FURI_LOG_I(LOGGING_TAG, "Migrated catalog. Result code: %d", (int)migration_result); + conf_file_exists = storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK; + } + + if(conf_file_exists) { FURI_LOG_D(LOGGING_TAG, "Config file %s found", CONFIG_FILE_PATH); if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH); @@ -119,16 +129,6 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { } } else { FURI_LOG_D(LOGGING_TAG, "Config file %s is not found. Will create new.", CONFIG_FILE_PATH); - if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { - FURI_LOG_D( - LOGGING_TAG, - "Directory %s doesn't exist. Will create new.", - CONFIG_FILE_DIRECTORY_PATH); - if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { - FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); - return false; - } - } if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) { totp_close_config_file(fff_data_file); @@ -139,6 +139,13 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { flipper_format_write_header_cstr( fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION); + uint32_t tmp_uint32 = CRYPTO_LATEST_VERSION; + flipper_format_write_uint32(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1); + + tmp_uint32 = DEFAULT_CRYPTO_KEY_SLOT; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1); + flipper_format_write_comment_cstr( fff_data_file, "Config file format specification can be found here: https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md"); @@ -146,7 +153,7 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { float tmp_tz = 0; flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); - uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; + tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); @@ -154,7 +161,11 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); - tmp_uint32 = 0; + tmp_uint32 = AutomationKeyboardLayoutQWERTY; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1); + + tmp_uint32 = 0; //-V1048 flipper_format_write_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1); if(!flipper_format_rewind(fff_data_file)) { @@ -233,6 +244,12 @@ bool totp_config_file_update_automation_method(const PluginState* plugin_state) break; } + tmp_uint32 = plugin_state->automation_kb_layout; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + break; + } + update_result = true; } while(false); @@ -265,6 +282,12 @@ bool totp_config_file_update_user_settings(const PluginState* plugin_state) { break; } + tmp_uint32 = plugin_state->automation_kb_layout; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + break; + } + update_result = true; } while(false); @@ -344,8 +367,38 @@ bool totp_config_file_load(PluginState* const plugin_state) { } } + uint32_t tmp_uint32; + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1)) { + FURI_LOG_E(LOGGING_TAG, "Missing required " TOTP_CONFIG_KEY_CRYPTO_VERSION "property"); + break; + } + + plugin_state->crypto_settings.crypto_version = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1)) { + FURI_LOG_E( + LOGGING_TAG, "Missing required " TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT "property"); + break; + } + + plugin_state->crypto_settings.crypto_key_slot = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + if(!flipper_format_read_hex( - fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) { + fff_data_file, + TOTP_CONFIG_KEY_BASE_IV, + &plugin_state->crypto_settings.base_iv[0], + CRYPTO_IV_LENGTH)) { FURI_LOG_D(LOGGING_TAG, "Missing base IV"); } @@ -357,22 +410,23 @@ bool totp_config_file_load(PluginState* const plugin_state) { if(flipper_format_get_value_count( fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) && crypto_size > 0) { - plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size); - furi_check(plugin_state->crypto_verify_data != NULL); - plugin_state->crypto_verify_data_length = crypto_size; + plugin_state->crypto_settings.crypto_verify_data = + malloc(sizeof(uint8_t) * crypto_size); + furi_check(plugin_state->crypto_settings.crypto_verify_data != NULL); + plugin_state->crypto_settings.crypto_verify_data_length = crypto_size; if(!flipper_format_read_hex( fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, + plugin_state->crypto_settings.crypto_verify_data, crypto_size)) { FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token"); - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; - plugin_state->crypto_verify_data_length = 0; + free(plugin_state->crypto_settings.crypto_verify_data); + plugin_state->crypto_settings.crypto_verify_data = NULL; + plugin_state->crypto_settings.crypto_verify_data_length = 0; } } else { - plugin_state->crypto_verify_data = NULL; - plugin_state->crypto_verify_data_length = 0; + plugin_state->crypto_settings.crypto_verify_data = NULL; + plugin_state->crypto_settings.crypto_verify_data_length = 0; } if(!flipper_format_rewind(fff_data_file)) { @@ -390,13 +444,17 @@ bool totp_config_file_load(PluginState* const plugin_state) { } if(!flipper_format_read_bool( - fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - plugin_state->pin_set = true; + fff_data_file, + TOTP_CONFIG_KEY_PINSET, + &plugin_state->crypto_settings.pin_required, + 1)) { + plugin_state->crypto_settings.pin_required = true; } - flipper_format_rewind(fff_data_file); + if(!flipper_format_rewind(fff_data_file)) { + break; + } - uint32_t tmp_uint32; if(!flipper_format_read_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; @@ -404,7 +462,9 @@ bool totp_config_file_load(PluginState* const plugin_state) { plugin_state->notification_method = tmp_uint32; - flipper_format_rewind(fff_data_file); + if(!flipper_format_rewind(fff_data_file)) { + break; + } if(!flipper_format_read_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { @@ -413,6 +473,21 @@ bool totp_config_file_load(PluginState* const plugin_state) { plugin_state->automation_method = tmp_uint32; + if(!flipper_format_rewind(fff_data_file)) { + break; + } + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + tmp_uint32 = AutomationKeyboardLayoutQWERTY; + } + + plugin_state->automation_kb_layout = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + if(!flipper_format_read_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1)) { tmp_uint32 = 0; } @@ -425,7 +500,9 @@ bool totp_config_file_load(PluginState* const plugin_state) { plugin_state->config_file_context->config_file = fff_data_file; plugin_state->config_file_context->token_info_iterator_context = totp_token_info_iterator_alloc( - storage, plugin_state->config_file_context->config_file, plugin_state->iv); + storage, + plugin_state->config_file_context->config_file, + &plugin_state->crypto_settings); result = true; } while(false); @@ -438,21 +515,39 @@ bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state) flipper_format_rewind(config_file); bool update_result = false; do { + uint32_t tmp_uint32 = plugin_state->crypto_settings.crypto_version; + if(!flipper_format_insert_or_update_uint32( + config_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = plugin_state->crypto_settings.crypto_key_slot; + if(!flipper_format_insert_or_update_uint32( + config_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1)) { + break; + } + if(!flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { + config_file, + TOTP_CONFIG_KEY_BASE_IV, + plugin_state->crypto_settings.base_iv, + CRYPTO_IV_LENGTH)) { break; } if(!flipper_format_insert_or_update_hex( config_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { + plugin_state->crypto_settings.crypto_verify_data, + plugin_state->crypto_settings.crypto_verify_data_length)) { break; } if(!flipper_format_insert_or_update_bool( - config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + config_file, + TOTP_CONFIG_KEY_PINSET, + &plugin_state->crypto_settings.pin_required, + 1)) { break; } @@ -480,6 +575,7 @@ void totp_config_file_reset(PluginState* const plugin_state) { bool totp_config_file_update_encryption( PluginState* plugin_state, + uint8_t new_crypto_key_slot, const uint8_t* new_pin, uint8_t new_pin_length) { FlipperFormat* config_file = plugin_state->config_file_context->config_file; @@ -489,23 +585,28 @@ bool totp_config_file_update_encryption( return false; } - uint8_t old_iv[TOTP_IV_SIZE]; - memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); - - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; + if(!totp_crypto_check_key_slot(new_crypto_key_slot)) { + return false; } - CryptoSeedIVResult seed_result = - totp_crypto_seed_iv(plugin_state, new_pin_length > 0 ? new_pin : NULL, new_pin_length); + CryptoSettings old_crypto_settings = plugin_state->crypto_settings; + + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); + memset(&plugin_state->crypto_settings.base_iv[0], 0, CRYPTO_IV_LENGTH); + if(plugin_state->crypto_settings.crypto_verify_data != NULL) { + free(plugin_state->crypto_settings.crypto_verify_data); + plugin_state->crypto_settings.crypto_verify_data = NULL; + } + + plugin_state->crypto_settings.crypto_key_slot = new_crypto_key_slot; + plugin_state->crypto_settings.crypto_version = CRYPTO_LATEST_VERSION; + + CryptoSeedIVResult seed_result = totp_crypto_seed_iv( + &plugin_state->crypto_settings, new_pin_length > 0 ? new_pin : NULL, new_pin_length); if(seed_result & CryptoSeedIVResultFlagSuccess && - seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { - if(!totp_config_file_update_crypto_signatures(plugin_state)) { - return false; - } + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData && + !totp_config_file_update_crypto_signatures(plugin_state)) { + return false; } else if(seed_result == CryptoSeedIVResultFailed) { return false; } @@ -552,12 +653,15 @@ bool totp_config_file_update_encryption( size_t plain_token_length; uint8_t* plain_token = totp_crypto_decrypt( - encrypted_token, secret_bytes_count, &old_iv[0], &plain_token_length); + encrypted_token, secret_bytes_count, &old_crypto_settings, &plain_token_length); free(encrypted_token); size_t encrypted_token_length; encrypted_token = totp_crypto_encrypt( - plain_token, plain_token_length, &plugin_state->iv[0], &encrypted_token_length); + plain_token, + plain_token_length, + &plugin_state->crypto_settings, + &encrypted_token_length); memset_s(plain_token, plain_token_length, 0, plain_token_length); free(plain_token); @@ -588,6 +692,36 @@ bool totp_config_file_update_encryption( return result; } +bool totp_config_file_ensure_latest_encryption( + PluginState* plugin_state, + const uint8_t* pin, + uint8_t pin_length) { + bool result = true; + if(plugin_state->crypto_settings.crypto_version < CRYPTO_LATEST_VERSION) { + FURI_LOG_I(LOGGING_TAG, "Migration to crypto v%d is needed", CRYPTO_LATEST_VERSION); + char* backup_path = totp_config_file_backup(plugin_state); + if(backup_path != NULL) { + free(backup_path); + uint8_t crypto_key_slot = plugin_state->crypto_settings.crypto_key_slot; + if(!totp_crypto_check_key_slot(crypto_key_slot)) { + crypto_key_slot = DEFAULT_CRYPTO_KEY_SLOT; + } + + result = + totp_config_file_update_encryption(plugin_state, crypto_key_slot, pin, pin_length); + FURI_LOG_I( + LOGGING_TAG, + "Migration to crypto v%d is done. Result: %d", + CRYPTO_LATEST_VERSION, + result); + } else { + result = false; + } + } + + return result; +} + TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state) { return plugin_state->config_file_context->token_info_iterator_context; } diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index d2fe957c6..38bc06ba2 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -73,15 +73,29 @@ void totp_config_file_close(PluginState* const plugin_state); /** * @brief Updates config file encryption by re-encrypting it using new user's PIN and new randomly generated IV * @param plugin_state application state + * @param new_crypto_key_slot new crypto key slot to be used * @param new_pin new user's PIN * @param new_pin_length new user's PIN length * @return \c true if config file encryption successfully updated; \c false otherwise */ bool totp_config_file_update_encryption( PluginState* plugin_state, + uint8_t new_crypto_key_slot, const uint8_t* new_pin, uint8_t new_pin_length); +/** + * @brief Ensures application config file uses latest encryption and upgrades encryption if needed + * @param plugin_state application state + * @param pin user's PIN + * @param pin_length user's PIN length + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_config_file_ensure_latest_encryption( + PluginState* plugin_state, + const uint8_t* pin, + uint8_t pin_length); + /** * @brief Gets token info iterator context * @param plugin_state application state diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index ba6658fb5..1205bae07 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -2,9 +2,9 @@ #include -#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") +#define CONFIG_FILE_DIRECTORY_PATH STORAGE_APP_DATA_PATH_PREFIX #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (6) +#define CONFIG_FILE_ACTUAL_VERSION (8) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" @@ -18,4 +18,7 @@ #define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" #define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod" +#define TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT "AutomationKbLayout" #define TOTP_CONFIG_KEY_FONT "Font" +#define TOTP_CONFIG_KEY_CRYPTO_VERSION "CryptoVersion" +#define TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT "CryptoKeySlot" diff --git a/applications/external/totp/services/config/migrations/common_migration.c b/applications/external/totp/services/config/migrations/common_migration.c index e8537da0b..4a12cbd3a 100644 --- a/applications/external/totp/services/config/migrations/common_migration.c +++ b/applications/external/totp/services/config/migrations/common_migration.c @@ -1,6 +1,7 @@ #include "common_migration.h" #include "../constants.h" #include "../../../types/token_info.h" +#include "../../../types/automation_kb_layout.h" #include bool totp_config_migrate_to_latest( @@ -17,6 +18,28 @@ bool totp_config_migrate_to_latest( break; } + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, temp_str); + } else { + uint32_t old_crypto_version = 1; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &old_crypto_version, 1); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, temp_str); + } else { + uint32_t default_old_key_slot = 2; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &default_old_key_slot, 1); + } + + flipper_format_rewind(fff_backup_data_file); + if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str)) { flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str); } @@ -68,6 +91,21 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, temp_str)) { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, temp_str); + } else { + uint32_t default_automation_kb_layout = AutomationKeyboardLayoutQWERTY; + flipper_format_write_uint32( + fff_data_file, + TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, + &default_automation_kb_layout, + 1); + } + + flipper_format_rewind(fff_backup_data_file); + while(true) { if(!flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c index 1f4a75776..75424ef43 100644 --- a/applications/external/totp/services/config/token_info_iterator.c +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -4,6 +4,7 @@ #include #include #include "../../types/common.h" +#include "../../types/crypto_settings.h" #define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part" #define STREAM_COPY_BUFFER_SIZE 128 @@ -15,7 +16,7 @@ struct TokenInfoIteratorContext { size_t last_seek_index; TokenInfo* current_token; FlipperFormat* config_file; - uint8_t* iv; + CryptoSettings* crypto_settings; Storage* storage; }; @@ -237,8 +238,10 @@ static bool return result; } -TokenInfoIteratorContext* - totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv) { +TokenInfoIteratorContext* totp_token_info_iterator_alloc( + Storage* storage, + FlipperFormat* config_file, + CryptoSettings* crypto_settings) { Stream* stream = flipper_format_get_raw_stream(config_file); stream_rewind(stream); size_t tokens_count = 0; @@ -256,7 +259,7 @@ TokenInfoIteratorContext* context->total_count = tokens_count; context->current_token = token_info_alloc(); context->config_file = config_file; - context->iv = iv; + context->crypto_settings = crypto_settings; context->storage = storage; return context; } @@ -453,7 +456,7 @@ bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t to furi_string_get_cstr(temp_str), furi_string_size(temp_str), PlainTokenSecretEncodingBase32, - context->iv)) { + context->crypto_settings)) { FURI_LOG_W( LOGGING_TAG, "Token \"%s\" has plain secret", diff --git a/applications/external/totp/services/config/token_info_iterator.h b/applications/external/totp/services/config/token_info_iterator.h index 7e9a65853..ce4d8c72b 100644 --- a/applications/external/totp/services/config/token_info_iterator.h +++ b/applications/external/totp/services/config/token_info_iterator.h @@ -28,11 +28,13 @@ enum TotpIteratorUpdateTokenResults { * @brief Initializes a new token info iterator * @param storage storage reference * @param config_file config file to use - * @param iv initialization vector (IV) to be used for encryption\decryption + * @param crypto_settings crypto settings * @return Token info iterator context */ -TokenInfoIteratorContext* - totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv); +TokenInfoIteratorContext* totp_token_info_iterator_alloc( + Storage* storage, + FlipperFormat* config_file, + CryptoSettings* crypto_settings); /** * @brief Navigates iterator to the token with given index diff --git a/applications/external/totp/services/crypto/common_types.h b/applications/external/totp/services/crypto/common_types.h new file mode 100644 index 000000000..fb8e4c83c --- /dev/null +++ b/applications/external/totp/services/crypto/common_types.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef uint8_t CryptoSeedIVResult; + +enum CryptoSeedIVResults { + + /** + * @brief IV seeding operation failed + */ + CryptoSeedIVResultFailed = 0b00, + + /** + * @brief IV seeding operation succeeded + */ + CryptoSeedIVResultFlagSuccess = 0b01, + + /** + * @brief As a part of IV seeding operation new crypto verify data has been generated + */ + CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 +}; \ No newline at end of file diff --git a/applications/external/totp/services/crypto/constants.h b/applications/external/totp/services/crypto/constants.h new file mode 100644 index 000000000..294ae4361 --- /dev/null +++ b/applications/external/totp/services/crypto/constants.h @@ -0,0 +1,11 @@ +#pragma once + +#define CRYPTO_IV_LENGTH (16) + +// According to this explanation: https://github.com/flipperdevices/flipperzero-firmware/issues/2885#issuecomment-1646664666 +// disabling usage of any key which is "the same across all devices" +#define ACCEPTABLE_CRYPTO_KEY_SLOT_START (12) +#define ACCEPTABLE_CRYPTO_KEY_SLOT_END (100) + +#define DEFAULT_CRYPTO_KEY_SLOT ACCEPTABLE_CRYPTO_KEY_SLOT_START +#define CRYPTO_LATEST_VERSION (2) \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_facade.c b/applications/external/totp/services/crypto/crypto_facade.c new file mode 100644 index 000000000..acbc09a4e --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_facade.c @@ -0,0 +1,78 @@ +#include "crypto_facade.h" +#include +#include +#include "crypto_v1.h" +#include "crypto_v2.h" +#include "constants.h" + +bool totp_crypto_check_key_slot(uint8_t key_slot) { + uint8_t empty_iv[CRYPTO_IV_LENGTH] = {0}; + if(key_slot < ACCEPTABLE_CRYPTO_KEY_SLOT_START || key_slot > ACCEPTABLE_CRYPTO_KEY_SLOT_END) { + return false; + } + + return furi_hal_crypto_verify_key(key_slot) && + furi_hal_crypto_store_load_key(key_slot, empty_iv) && + furi_hal_crypto_store_unload_key(key_slot); +} + +uint8_t* totp_crypto_encrypt( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length) { + if(crypto_settings->crypto_version == 1) { + return totp_crypto_encrypt_v1( + plain_data, plain_data_length, crypto_settings, encrypted_data_length); + } + + if(crypto_settings->crypto_version == 2) { + return totp_crypto_encrypt_v2( + plain_data, plain_data_length, crypto_settings, encrypted_data_length); + } + + furi_crash("Unsupported crypto version"); +} + +uint8_t* totp_crypto_decrypt( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length) { + if(crypto_settings->crypto_version == 1) { + return totp_crypto_decrypt_v1( + encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); + } + + if(crypto_settings->crypto_version == 2) { + return totp_crypto_decrypt_v2( + encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); + } + + furi_crash("Unsupported crypto version"); +} + +CryptoSeedIVResult + totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length) { + if(crypto_settings->crypto_version == 1) { + return totp_crypto_seed_iv_v1(crypto_settings, pin, pin_length); + } + + if(crypto_settings->crypto_version == 2) { + return totp_crypto_seed_iv_v2(crypto_settings, pin, pin_length); + } + + furi_crash("Unsupported crypto version"); +} + +bool totp_crypto_verify_key(const CryptoSettings* crypto_settings) { + if(crypto_settings->crypto_version == 1) { + return totp_crypto_verify_key_v1(crypto_settings); + } + + if(crypto_settings->crypto_version == 2) { + return totp_crypto_verify_key_v2(crypto_settings); + } + + furi_crash("Unsupported crypto version"); +} \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto_facade.h similarity index 58% rename from applications/external/totp/services/crypto/crypto.h rename to applications/external/totp/services/crypto/crypto_facade.h index ab27191a8..bbcbf7c00 100644 --- a/applications/external/totp/services/crypto/crypto.h +++ b/applications/external/totp/services/crypto/crypto_facade.h @@ -1,68 +1,59 @@ #pragma once -#include "../../types/plugin_state.h" +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" -typedef uint8_t CryptoSeedIVResult; - -enum CryptoSeedIVResults { - - /** - * @brief IV seeding operation failed - */ - CryptoSeedIVResultFailed = 0b00, - - /** - * @brief IV seeding operation succeeded - */ - CryptoSeedIVResultFlagSuccess = 0b01, - - /** - * @brief As a part of IV seeding operation new crypto verify data has been generated - */ - CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 -}; +/** + * @brief Checks whether key slot can be used for encryption purposes + * @param key_slot key slot index + * @return \c true if key slot can be used for encryption; \c false otherwise + */ +bool totp_crypto_check_key_slot(uint8_t key_slot); /** * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) * @param plain_data plain data to be encrypted * @param plain_data_length plain data length - * @param iv initialization vector (IV) to be used to encrypt plain data + * @param crypto_settings crypto settings * @param[out] encrypted_data_length encrypted data length * @return Encrypted data */ uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, const size_t plain_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* encrypted_data_length); /** * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) * @param encrypted_data encrypted data to be decrypted * @param encrypted_data_length encrypted data length - * @param iv initialization vector (IV) to be used to encrypt plain data + * @param crypto_settings crypto settings * @param[out] decrypted_data_length decrypted data length * @return Decrypted data */ uint8_t* totp_crypto_decrypt( const uint8_t* encrypted_data, const size_t encrypted_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* decrypted_data_length); /** * @brief Seed initialization vector (IV) using user's PIN - * @param plugin_state application state + * @param crypto_settings crypto settings * @param pin user's PIN * @param pin_length user's PIN length * @return Results of seeding IV */ CryptoSeedIVResult - totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); + totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); /** * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption - * @param plugin_state application state + * @param crypto_settings crypto settings * @return \c true if cryptographic information is valid; \c false otherwise */ -bool totp_crypto_verify_key(const PluginState* plugin_state); \ No newline at end of file +bool totp_crypto_verify_key(const CryptoSettings* crypto_settings); diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto_v1.c similarity index 66% rename from applications/external/totp/services/crypto/crypto.c rename to applications/external/totp/services/crypto/crypto_v1.c index 95ddc3210..d637599f5 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto_v1.c @@ -1,4 +1,6 @@ -#include "crypto.h" +#include "crypto_v1.h" +#include +#include #include #include #include @@ -8,13 +10,14 @@ #define CRYPTO_KEY_SLOT (2) #define CRYPTO_VERIFY_KEY_LENGTH (16) #define CRYPTO_ALIGNMENT_FACTOR (16) +#define TOTP_IV_SIZE (16) static const char* CRYPTO_VERIFY_KEY = "FFF_Crypto_pass"; -uint8_t* totp_crypto_encrypt( +uint8_t* totp_crypto_encrypt_v1( const uint8_t* plain_data, const size_t plain_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* encrypted_data_length) { uint8_t* encrypted_data; size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; @@ -29,7 +32,7 @@ uint8_t* totp_crypto_encrypt( furi_check(encrypted_data != NULL); *encrypted_data_length = plain_data_aligned_length; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length); furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); @@ -40,7 +43,7 @@ uint8_t* totp_crypto_encrypt( furi_check(encrypted_data != NULL); *encrypted_data_length = plain_data_length; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length); furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); } @@ -48,29 +51,31 @@ uint8_t* totp_crypto_encrypt( return encrypted_data; } -uint8_t* totp_crypto_decrypt( +uint8_t* totp_crypto_decrypt_v1( const uint8_t* encrypted_data, const size_t encrypted_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* decrypted_data_length) { *decrypted_data_length = encrypted_data_length; uint8_t* decrypted_data = malloc(*decrypted_data_length); furi_check(decrypted_data != NULL); - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length); furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); return decrypted_data; } -CryptoSeedIVResult - totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { +CryptoSeedIVResult totp_crypto_seed_iv_v1( + CryptoSettings* crypto_settings, + const uint8_t* pin, + uint8_t pin_length) { CryptoSeedIVResult result; - if(plugin_state->crypto_verify_data == NULL) { + if(crypto_settings->crypto_verify_data == NULL) { FURI_LOG_I(LOGGING_TAG, "Generating new IV"); - furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); + furi_hal_random_fill_buf(&crypto_settings->base_iv[0], TOTP_IV_SIZE); } - memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE); + memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], TOTP_IV_SIZE); if(pin != NULL && pin_length > 0) { uint8_t max_i; if(pin_length > TOTP_IV_SIZE) { @@ -80,7 +85,7 @@ CryptoSeedIVResult } for(uint8_t i = 0; i < max_i; i++) { - plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1)); + crypto_settings->iv[i] = crypto_settings->iv[i] ^ (uint8_t)(pin[i] * (i + 1)); } } else { uint8_t max_i; @@ -93,24 +98,24 @@ CryptoSeedIVResult const uint8_t* uid = (const uint8_t*)UID64_BASE; //-V566 for(uint8_t i = 0; i < max_i; i++) { - plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; + crypto_settings->iv[i] = crypto_settings->iv[i] ^ uid[i]; } } result = CryptoSeedIVResultFlagSuccess; - if(plugin_state->crypto_verify_data == NULL) { + if(crypto_settings->crypto_verify_data == NULL) { FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); - plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); - furi_check(plugin_state->crypto_verify_data != NULL); - plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; + crypto_settings->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); + furi_check(crypto_settings->crypto_verify_data != NULL); + crypto_settings->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; - plugin_state->crypto_verify_data = totp_crypto_encrypt( + crypto_settings->crypto_verify_data = totp_crypto_encrypt_v1( (const uint8_t*)CRYPTO_VERIFY_KEY, CRYPTO_VERIFY_KEY_LENGTH, - &plugin_state->iv[0], - &plugin_state->crypto_verify_data_length); + crypto_settings, + &crypto_settings->crypto_verify_data_length); - plugin_state->pin_set = pin != NULL && pin_length > 0; + crypto_settings->pin_required = pin != NULL && pin_length > 0; result |= CryptoSeedIVResultFlagNewCryptoVerifyData; } @@ -118,12 +123,12 @@ CryptoSeedIVResult return result; } -bool totp_crypto_verify_key(const PluginState* plugin_state) { +bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings) { size_t decrypted_key_length; - uint8_t* decrypted_key = totp_crypto_decrypt( - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length, - &plugin_state->iv[0], + uint8_t* decrypted_key = totp_crypto_decrypt_v1( + crypto_settings->crypto_verify_data, + crypto_settings->crypto_verify_data_length, + crypto_settings, &decrypted_key_length); bool key_valid = true; diff --git a/applications/external/totp/services/crypto/crypto_v1.h b/applications/external/totp/services/crypto/crypto_v1.h new file mode 100644 index 000000000..80e850196 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v1.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param crypto_settings crypto settings + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt_v1( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param crypto_settings crypto settings + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt_v1( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param crypto_settings crypto settings + * @param pin user's PIN + * @param pin_length user's PIN length + * @return Results of seeding IV + */ +CryptoSeedIVResult + totp_crypto_seed_iv_v1(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param crypto_settings crypto settings + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings); \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v2.c b/applications/external/totp/services/crypto/crypto_v2.c new file mode 100644 index 000000000..897a7fd32 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v2.c @@ -0,0 +1,184 @@ +#include "crypto_v2.h" +#include +#include +#include +#include +#include +#include "../../types/common.h" +#include "../hmac/hmac_sha512.h" +#include "memset_s.h" +#include "constants.h" + +#define CRYPTO_ALIGNMENT_FACTOR (16) + +static const uint8_t* get_device_uid() { + return (const uint8_t*)UID64_BASE; //-V566 +} + +static uint8_t get_device_uid_length() { + return furi_hal_version_uid_size(); +} + +static const uint8_t* get_crypto_verify_key() { + return get_device_uid(); +} + +static uint8_t get_crypto_verify_key_length() { + return get_device_uid_length(); +} + +uint8_t* totp_crypto_encrypt_v2( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length) { + uint8_t* encrypted_data; + size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; + if(remain) { + size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; + uint8_t* plain_data_aligned = malloc(plain_data_aligned_length); + furi_check(plain_data_aligned != NULL); + memset(plain_data_aligned, 0, plain_data_aligned_length); + memcpy(plain_data_aligned, plain_data, plain_data_length); + + encrypted_data = malloc(plain_data_aligned_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_aligned_length; + + furi_check( + furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: store_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: store_unload_key"); + + memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); + free(plain_data_aligned); + } else { + encrypted_data = malloc(plain_data_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_length; + + furi_check( + furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: store_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: store_unload_key"); + } + + return encrypted_data; +} + +uint8_t* totp_crypto_decrypt_v2( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length) { + *decrypted_data_length = encrypted_data_length; + uint8_t* decrypted_data = malloc(*decrypted_data_length); + furi_check(decrypted_data != NULL); + furi_check( + furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Decryption failed: store_load_key"); + furi_check( + furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length), + "Decryption failed: decrypt"); + furi_check( + furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot), + "Decryption failed: store_unload_key"); + return decrypted_data; +} + +CryptoSeedIVResult totp_crypto_seed_iv_v2( + CryptoSettings* crypto_settings, + const uint8_t* pin, + uint8_t pin_length) { + CryptoSeedIVResult result; + if(crypto_settings->crypto_verify_data == NULL) { + FURI_LOG_I(LOGGING_TAG, "Generating new IV"); + furi_hal_random_fill_buf(&crypto_settings->base_iv[0], CRYPTO_IV_LENGTH); + } + + memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH); + + const uint8_t* device_uid = get_device_uid(); + uint8_t device_uid_length = get_device_uid_length(); + + uint8_t hmac_key_length = device_uid_length; + if(pin != NULL && pin_length > 0) { + hmac_key_length += pin_length; + } + + uint8_t* hmac_key = malloc(hmac_key_length); + furi_check(hmac_key != NULL); + + memcpy(hmac_key, device_uid, device_uid_length); + + if(pin != NULL && pin_length > 0) { + memcpy(hmac_key + device_uid_length, pin, pin_length); + } + + uint8_t hmac[HMAC_SHA512_RESULT_SIZE] = {0}; + int hmac_result_code = hmac_sha512( + hmac_key, hmac_key_length, &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH, &hmac[0]); + + memset_s(hmac_key, hmac_key_length, 0, hmac_key_length); + free(hmac_key); + + if(hmac_result_code == 0) { + uint8_t offset = + hmac[HMAC_SHA512_RESULT_SIZE - 1] % (HMAC_SHA512_RESULT_SIZE - CRYPTO_IV_LENGTH - 1); + memcpy(&crypto_settings->iv[0], &hmac[offset], CRYPTO_IV_LENGTH); + + result = CryptoSeedIVResultFlagSuccess; + if(crypto_settings->crypto_verify_data == NULL) { + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); + crypto_settings->crypto_verify_data = malloc(crypto_vkey_length); + furi_check(crypto_settings->crypto_verify_data != NULL); + crypto_settings->crypto_verify_data_length = crypto_vkey_length; + + crypto_settings->crypto_verify_data = totp_crypto_encrypt_v2( + crypto_vkey, + crypto_vkey_length, + crypto_settings, + &crypto_settings->crypto_verify_data_length); + + crypto_settings->pin_required = pin != NULL && pin_length > 0; + + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; + } + } else { + result = CryptoSeedIVResultFailed; + } + + return result; +} + +bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings) { + size_t decrypted_key_length; + uint8_t* decrypted_key = totp_crypto_decrypt_v2( + crypto_settings->crypto_verify_data, + crypto_settings->crypto_verify_data_length, + crypto_settings, + &decrypted_key_length); + + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + bool key_valid = true; + for(uint8_t i = 0; i < crypto_vkey_length && key_valid; i++) { + if(decrypted_key[i] != crypto_vkey[i]) key_valid = false; + } + + free(decrypted_key); + + return key_valid; +} \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v2.h b/applications/external/totp/services/crypto/crypto_v2.h new file mode 100644 index 000000000..1395e7046 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v2.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param crypto_settings crypto settings + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt_v2( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param crypto_settings crypto settings + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt_v2( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param crypto_settings crypto settings + * @param pin user's PIN + * @param pin_length user's PIN length + * @return Results of seeding IV + */ +CryptoSeedIVResult + totp_crypto_seed_iv_v2(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param crypto_settings crypto settings + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings); \ No newline at end of file diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 71d30d704..9322589b4 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -14,60 +14,53 @@ #include "ui/scene_director.h" #include "ui/constants.h" #include "ui/common_dialogs.h" -#include "services/crypto/crypto.h" +#include "services/crypto/crypto_facade.h" #include "cli/cli.h" +#include "version.h" -static void render_callback(Canvas* const canvas, void* ctx) { +struct TotpRenderCallbackContext { + FuriMutex* mutex; + PluginState* plugin_state; +}; + +static void render_callback(Canvas* const canvas, void* const ctx) { furi_assert(ctx); - PluginState* plugin_state = ctx; - if(furi_mutex_acquire(plugin_state->mutex, 25) == FuriStatusOk) { - totp_scene_director_render(canvas, plugin_state); - furi_mutex_release(plugin_state->mutex); + const struct TotpRenderCallbackContext* context = ctx; + if(furi_mutex_acquire(context->mutex, 25) == FuriStatusOk) { + totp_scene_director_render(canvas, context->plugin_state); + furi_mutex_release(context->mutex); } } -static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - +static void input_callback(InputEvent* const input_event, void* const ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; PluginEvent event = {.type = EventTypeKey, .input = *input_event}; furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static bool totp_activate_initial_scene(PluginState* const plugin_state) { - if(plugin_state->crypto_verify_data == NULL) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "No", NULL, "Yes"); - dialog_message_set_text( - message, - "Would you like to setup PIN?", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - DialogMessageButton dialog_result = - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); - if(dialog_result == DialogMessageButtonRight) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); - } else { - CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); - if(seed_result & CryptoSeedIVResultFlagSuccess && - seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { - if(!totp_config_file_update_crypto_signatures(plugin_state)) { - totp_dialogs_config_loading_error(plugin_state); - return false; - } - } else if(seed_result == CryptoSeedIVResultFailed) { - totp_dialogs_config_loading_error(plugin_state); - return false; - } +static bool first_run_init(PluginState* const plugin_state) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_text( + message, + "Would you like to setup PIN?", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + if(!totp_crypto_check_key_slot(plugin_state->crypto_settings.crypto_key_slot)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); - } - } else if(plugin_state->pin_set) { + if(dialog_result == DialogMessageButtonRight) { totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + CryptoSeedIVResult seed_result = + totp_crypto_seed_iv(&plugin_state->crypto_settings, NULL, 0); if(seed_result & CryptoSeedIVResultFlagSuccess && seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { if(!totp_config_file_update_crypto_signatures(plugin_state)) { @@ -79,23 +72,65 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { return false; } - if(totp_crypto_verify_key(plugin_state)) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); - } else { - FURI_LOG_E( - LOGGING_TAG, - "Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other"); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "Exit", NULL, NULL); - dialog_message_set_text( - message, - "Digital signature verification failed", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } + + return true; +} + +static bool pinless_activation(PluginState* const plugin_state) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(&plugin_state->crypto_settings, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + + if(totp_crypto_verify_key(&plugin_state->crypto_settings)) { + totp_config_file_ensure_latest_encryption(plugin_state, NULL, 0); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } else { + FURI_LOG_E( + LOGGING_TAG, + "Digital signature verification failed. Looks like conf file was created on another device and can't be used on any other"); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Exit", NULL, NULL); + dialog_message_set_text( + message, + "Digital signature verification failed", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + return false; + } + + return true; +} + +static bool pin_activation(PluginState* const plugin_state) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); + return true; +} + +static bool totp_activate_initial_scene(PluginState* const plugin_state) { + if(plugin_state->crypto_settings.crypto_verify_data == NULL) { + if(!first_run_init(plugin_state)) { + return false; + } + } else if(plugin_state->crypto_settings.pin_required) { + if(!pin_activation(plugin_state)) { + return false; + } + } else { + if(!pinless_activation(plugin_state)) { return false; } } @@ -108,6 +143,7 @@ static bool on_user_idle(void* context) { if(plugin_state->current_scene != TotpSceneAuthentication && plugin_state->current_scene != TotpSceneStandby) { totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); + totp_scene_director_force_redraw(plugin_state); return true; } @@ -116,18 +152,17 @@ static bool on_user_idle(void* context) { static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->gui = furi_record_open(RECORD_GUI); - plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION); plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); if(!totp_config_file_load(plugin_state)) { totp_dialogs_config_loading_error(plugin_state); return false; } - plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init(); } else { @@ -135,7 +170,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { } #endif - if(plugin_state->pin_set) { + if(plugin_state->crypto_settings.pin_required) { plugin_state->idle_timeout_context = idle_timeout_alloc(TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC, &on_user_idle, plugin_state); idle_timeout_start(plugin_state->idle_timeout_context); @@ -153,28 +188,35 @@ static void totp_plugin_state_free(PluginState* plugin_state) { } furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); totp_config_file_close(plugin_state); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); + if(plugin_state->crypto_settings.crypto_verify_data != NULL) { + free(plugin_state->crypto_settings.crypto_verify_data); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); plugin_state->bt_type_code_worker_context = NULL; } #endif - furi_mutex_free(plugin_state->mutex); + if(plugin_state->event_queue != NULL) { + furi_message_queue_free(plugin_state->event_queue); + } + free(plugin_state); } int32_t totp_app() { - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + FURI_LOG_I( + LOGGING_TAG, + "App version: %" PRIu8 ".%" PRIu8 ".%" PRIu8, + TOTP_APP_VERSION_MAJOR, + TOTP_APP_VERSION_MINOR, + TOTP_APP_VERSION_PATCH); PluginState* plugin_state = malloc(sizeof(PluginState)); furi_check(plugin_state != NULL); @@ -184,7 +226,7 @@ int32_t totp_app() { return 254; } - TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue); + TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state); if(!totp_activate_initial_scene(plugin_state)) { FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n"); @@ -192,10 +234,14 @@ int32_t totp_app() { return 253; } + FuriMutex* main_loop_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + struct TotpRenderCallbackContext render_context = { + .plugin_state = plugin_state, .mutex = main_loop_mutex}; + // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, plugin_state); - view_port_input_callback_set(view_port, input_callback, event_queue); + view_port_draw_callback_set(view_port, render_callback, &render_context); + view_port_input_callback_set(view_port, input_callback, plugin_state->event_queue); // Open GUI and register view_port gui_add_view_port(plugin_state->gui, view_port, GuiLayerFullscreen); @@ -203,24 +249,24 @@ int32_t totp_app() { PluginEvent event; bool processing = true; while(processing) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - - if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) { - if(event_status == FuriStatusOk) { + if(furi_message_queue_get(plugin_state->event_queue, &event, FuriWaitForever) == + FuriStatusOk) { + if(event.type == EventForceCloseApp) { + processing = false; + } else if(event.type == EventForceRedraw) { + processing = true; //-V1048 + } else if(furi_mutex_acquire(main_loop_mutex, FuriWaitForever) == FuriStatusOk) { if(event.type == EventTypeKey && plugin_state->idle_timeout_context != NULL) { idle_timeout_report_activity(plugin_state->idle_timeout_context); } - if(event.type == EventForceCloseApp) { - processing = false; - } else { - processing = totp_scene_director_handle_event(&event, plugin_state); - } - } + processing = totp_scene_director_handle_event(&event, plugin_state); - view_port_update(view_port); - furi_mutex_release(plugin_state->mutex); + furi_mutex_release(main_loop_mutex); + } } + + view_port_update(view_port); } totp_cli_unregister_command_handler(cli_context); @@ -229,7 +275,7 @@ int32_t totp_app() { view_port_enabled_set(view_port, false); gui_remove_view_port(plugin_state->gui, view_port); view_port_free(view_port); - furi_message_queue_free(event_queue); + furi_mutex_free(main_loop_mutex); totp_plugin_state_free(plugin_state); return 0; } diff --git a/applications/external/totp/types/automation_kb_layout.h b/applications/external/totp/types/automation_kb_layout.h new file mode 100644 index 000000000..9c23e91ab --- /dev/null +++ b/applications/external/totp/types/automation_kb_layout.h @@ -0,0 +1,8 @@ +#pragma once + +typedef uint8_t AutomationKeyboardLayout; + +enum AutomationKeyboardLayouts { + AutomationKeyboardLayoutQWERTY = 0, + AutomationKeyboardLayoutAZERTY = 1 +}; \ No newline at end of file diff --git a/applications/external/totp/types/automation_method.h b/applications/external/totp/types/automation_method.h index b51e59e03..89e39e18e 100644 --- a/applications/external/totp/types/automation_method.h +++ b/applications/external/totp/types/automation_method.h @@ -7,7 +7,7 @@ typedef uint8_t AutomationMethod; enum AutomationMethods { AutomationMethodNone = 0b00, AutomationMethodBadUsb = 0b01, -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED AutomationMethodBadBt = 0b10, #endif }; diff --git a/applications/external/totp/types/crypto_settings.h b/applications/external/totp/types/crypto_settings.h new file mode 100644 index 000000000..8b970fd6d --- /dev/null +++ b/applications/external/totp/types/crypto_settings.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include "../services/crypto/constants.h" + +typedef struct { + /** + * @brief Crypto key slot to be used + */ + uint8_t crypto_key_slot; + + /** + * @brief Crypto algorithms version to be used + */ + uint8_t crypto_version; + + /** + * @brief Initialization vector (IV) to be used for encryption\decryption + */ + uint8_t iv[CRYPTO_IV_LENGTH]; + + /** + * @brief Basic randomly-generated initialization vector (IV) + */ + uint8_t base_iv[CRYPTO_IV_LENGTH]; + + /** + * @brief Encrypted well-known data + */ + uint8_t* crypto_verify_data; + + /** + * @brief Encrypted well-known data length + */ + size_t crypto_verify_data_length; + + /** + * @brief Whether user's PIN is required or not + */ + bool pin_required; +} CryptoSettings; \ No newline at end of file diff --git a/applications/external/totp/types/event_type.h b/applications/external/totp/types/event_type.h index 7bdf6981f..138f528d8 100644 --- a/applications/external/totp/types/event_type.h +++ b/applications/external/totp/types/event_type.h @@ -3,4 +3,4 @@ typedef uint8_t EventType; -enum EventTypes { EventTypeTick, EventTypeKey, EventForceCloseApp }; +enum EventTypes { EventTypeTick, EventTypeKey, EventForceCloseApp, EventForceRedraw }; diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index 87ed51abd..a3b2f5c8f 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -9,11 +9,11 @@ #include "../services/idle_timeout/idle_timeout.h" #include "notification_method.h" #include "automation_method.h" -#ifdef TOTP_BADBT_TYPE_ENABLED +#include "automation_kb_layout.h" +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../workers/bt_type_code/bt_type_code.h" #endif - -#define TOTP_IV_SIZE (16) +#include "crypto_settings.h" /** * @brief Application state structure @@ -29,11 +29,6 @@ typedef struct { */ void* current_scene_state; - /** - * @brief Reference to the firmware notification subsystem - */ - NotificationApp* notification_app; - /** * @brief Reference to the firmware dialogs subsystem */ @@ -54,47 +49,22 @@ typedef struct { */ ConfigFileContext* config_file_context; - /** - * @brief Encrypted well-known string data - */ - uint8_t* crypto_verify_data; - - /** - * @brief Encrypted well-known string data length - */ - size_t crypto_verify_data_length; - - /** - * @brief Whether PIN is set by user or not - */ - bool pin_set; - - /** - * @brief Initialization vector (IV) to be used for encryption\decryption - */ - uint8_t iv[TOTP_IV_SIZE]; - - /** - * @brief Basic randomly-generated initialization vector (IV) - */ - uint8_t base_iv[TOTP_IV_SIZE]; - /** * @brief Notification method */ NotificationMethod notification_method; - /** - * @brief Main rendering loop mutex - */ - FuriMutex* mutex; - /** * @brief Automation method */ AutomationMethod automation_method; -#ifdef TOTP_BADBT_TYPE_ENABLED + /** + * @brief Automation keyboard layout to be used + */ + AutomationKeyboardLayout automation_kb_layout; + +#ifdef TOTP_BADBT_AUTOMATION_ENABLED /** * @brief Bad-Bluetooth worker context */ @@ -110,4 +80,14 @@ typedef struct { * @brief Font index to be used to draw TOTP token */ uint8_t active_font_index; + + /** + * @brief Application even queue + */ + FuriMessageQueue* event_queue; + + /** + * @brief Crypto settings + */ + CryptoSettings crypto_settings; } PluginState; diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index ab47f4e3e..1d1e73160 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -1,9 +1,10 @@ #include "token_info.h" +#include #include #include #include #include "common.h" -#include "../services/crypto/crypto.h" +#include "../services/crypto/crypto_facade.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); @@ -25,7 +26,7 @@ bool token_info_set_secret( const char* plain_token_secret, size_t token_secret_length, PlainTokenSecretEncoding plain_token_secret_encoding, - const uint8_t* iv) { + const CryptoSettings* crypto_settings) { if(token_secret_length == 0) return false; uint8_t* plain_secret; size_t plain_secret_length; @@ -54,8 +55,8 @@ bool token_info_set_secret( free(token_info->token); } - token_info->token = - totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); + token_info->token = totp_crypto_encrypt( + plain_secret, plain_secret_length, crypto_settings, &token_info->token_length); result = true; } else { result = false; diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 163a0492e..969445dff 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -1,8 +1,9 @@ #pragma once -#include +#include #include #include +#include "crypto_settings.h" #define TOKEN_HASH_ALGO_SHA1_NAME "sha1" #define TOKEN_HASH_ALGO_STEAM_NAME "steam" @@ -200,7 +201,7 @@ void token_info_free(TokenInfo* token_info); * @param plain_token_secret plain token secret * @param token_secret_length plain token secret length * @param plain_token_secret_encoding plain token secret encoding - * @param iv initialization vecor (IV) to be used for encryption + * @param crypto_settings crypto settings * @return \c true if token successfully set; \c false otherwise */ bool token_info_set_secret( @@ -208,7 +209,7 @@ bool token_info_set_secret( const char* plain_token_secret, size_t token_secret_length, PlainTokenSecretEncoding plain_token_secret_encoding, - const uint8_t* iv); + const CryptoSettings* crypto_settings); /** * @brief Sets token digits count from \c uint8_t value diff --git a/applications/external/totp/ui/canvas_extensions.h b/applications/external/totp/ui/canvas_extensions.h index ab5519140..2e053b488 100644 --- a/applications/external/totp/ui/canvas_extensions.h +++ b/applications/external/totp/ui/canvas_extensions.h @@ -4,6 +4,15 @@ #include #include +/** + * @brief Draw string using given font + * @param canvas canvas to draw string at + * @param x horizontal position + * @param y vertical position + * @param text string to draw + * @param text_length string length + * @param font font to be used to draw string + */ void canvas_draw_str_ex( Canvas* canvas, uint8_t x, diff --git a/applications/external/totp/ui/common_dialogs.h b/applications/external/totp/ui/common_dialogs.h index 187d0e95c..1ddd80a75 100644 --- a/applications/external/totp/ui/common_dialogs.h +++ b/applications/external/totp/ui/common_dialogs.h @@ -3,5 +3,16 @@ #include #include "../types/plugin_state.h" +/** + * @brief Shows standard dialog about the fact that error occurred when loading config file + * @param plugin_state application state + * @return dialog button which user pressed to close the dialog + */ DialogMessageButton totp_dialogs_config_loading_error(PluginState* plugin_state); + +/** + * @brief Shows standard dialog about the fact that error occurred when updating config file + * @param plugin_state application state + * @return dialog button which user pressed to close the dialog + */ DialogMessageButton totp_dialogs_config_updating_error(PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index 657762a94..fd7cf1dca 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -116,3 +116,8 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con return processing; } + +void totp_scene_director_force_redraw(PluginState* const plugin_state) { + PluginEvent event = {.type = EventForceRedraw}; + furi_message_queue_put(plugin_state->event_queue, &event, FuriWaitForever); +} \ No newline at end of file diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index e45223997..1f09f9ea9 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -33,3 +33,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ * @return \c true if event handled and applilcation should continue; \c false if application should be closed */ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state); + +/** + * @brief Forces screen to be redraw\updated + * @param plugin_state application state + */ +void totp_scene_director_force_redraw(PluginState* const plugin_state); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 6856d1e30..6ae288d4c 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -42,7 +42,7 @@ typedef struct { struct TotpAddContext { SceneState* scene_state; - uint8_t* iv; + const CryptoSettings* crypto_settings; }; enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 }; @@ -58,7 +58,7 @@ static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, con context_t->scene_state->token_secret, context_t->scene_state->token_secret_length, PlainTokenSecretEncodingBase32, - context_t->iv)) { + context_t->crypto_settings)) { return TotpIteratorUpdateTokenResultInvalidSecret; } @@ -271,7 +271,7 @@ bool totp_scene_add_new_token_handle_event( break; case ConfirmButton: { struct TotpAddContext add_context = { - .iv = plugin_state->iv, .scene_state = scene_state}; + .scene_state = scene_state, .crypto_settings = &plugin_state->crypto_settings}; TokenInfoIteratorContext* iterator_context = totp_config_get_token_iterator_context(plugin_state); TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token( diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index c65557cfc..0ee3ec6c4 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -13,14 +13,30 @@ #include "../../../services/convert/convert.h" #include #include "../../../features_config.h" -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif +#ifdef TOTP_BADBT_AUTOMATION_ENABLED +#define AUTOMATION_LIST_MAX_INDEX (3) +#else +#define AUTOMATION_LIST_MAX_INDEX (1) +#endif +#define BAD_KB_LAYOUT_LIST_MAX_INDEX (1) +#define FONT_TEST_STR_LENGTH (7) + static const char* YES_NO_LIST[] = {"NO", "YES"}; -static const char* ON_OFF_LIST[] = {"OFF", "ON"}; +static const char* AUTOMATION_LIST[] = { + "None", + "USB" +#ifdef TOTP_BADBT_AUTOMATION_ENABLED + , + "Bluetooth", + "BT and USB" +#endif +}; +static const char* BAD_KB_LAYOUT_LIST[] = {"QWERTY", "AZERTY"}; static const char* FONT_TEST_STR = "0123BCD"; -static const uint8_t FONT_TEST_STR_LENGTH = 7; typedef enum { HoursInput, @@ -28,10 +44,8 @@ typedef enum { FontSelect, SoundSwitch, VibroSwitch, - BadUsbSwitch, -#ifdef TOTP_BADBT_TYPE_ENABLED - BadBtSwitch, -#endif + AutomationSwitch, + BadKeyboardLayoutSelect, ConfirmButton } Control; @@ -40,11 +54,9 @@ typedef struct { uint8_t tz_offset_minutes; bool notification_sound; bool notification_vibro; - bool badusb_enabled; -#ifdef TOTP_BADBT_TYPE_ENABLED - bool badbt_enabled; -#endif - uint8_t y_offset; + AutomationMethod automation_method; + uint16_t y_offset; + AutomationKeyboardLayout automation_kb_layout; Control selected_control; uint8_t active_font; } SceneState; @@ -60,10 +72,11 @@ void totp_scene_app_settings_activate(PluginState* plugin_state) { scene_state->tz_offset_minutes = 60.0f * off_dec; scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound; scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro; - scene_state->badusb_enabled = plugin_state->automation_method & AutomationMethodBadUsb; -#ifdef TOTP_BADBT_TYPE_ENABLED - scene_state->badbt_enabled = plugin_state->automation_method & AutomationMethodBadBt; -#endif + scene_state->automation_method = + MIN(plugin_state->automation_method, AUTOMATION_LIST_MAX_INDEX); + scene_state->automation_kb_layout = + MIN(plugin_state->automation_kb_layout, BAD_KB_LAYOUT_LIST_MAX_INDEX); + scene_state->active_font = plugin_state->active_font_index; } @@ -83,127 +96,121 @@ static void two_digit_to_str(int8_t num, char* str) { void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) { const SceneState* scene_state = plugin_state->current_scene_state; + if(scene_state->selected_control < FontSelect) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); + canvas_set_font(canvas, FontSecondary); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); - canvas_set_font(canvas, FontSecondary); + char tmp_str[4]; + two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); + ui_control_select_render( + canvas, + 36, + 10 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + &tmp_str[0], + scene_state->selected_control == HoursInput); - char tmp_str[4]; - two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); - canvas_draw_str_aligned(canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); - ui_control_select_render( - canvas, - 36, - 10 - scene_state->y_offset, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == HoursInput); + two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); + ui_control_select_render( + canvas, + 36, + 28 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + &tmp_str[0], + scene_state->selected_control == MinutesInput); - two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); - canvas_draw_str_aligned( - canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); - ui_control_select_render( - canvas, - 36, - 28 - scene_state->y_offset, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == MinutesInput); + } else if(scene_state->selected_control < SoundSwitch) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font"); + canvas_set_font(canvas, FontSecondary); - canvas_draw_icon( - canvas, - SCREEN_WIDTH_CENTER - 5, - SCREEN_HEIGHT - 5 - scene_state->y_offset, - &I_totp_arrow_bottom_10x5); + const FONT_INFO* const font = available_fonts[scene_state->active_font]; + ui_control_select_render( + canvas, + 0, + 74 - scene_state->y_offset, + SCREEN_WIDTH - UI_CONTROL_VSCROLL_WIDTH, + font->name, + scene_state->selected_control == FontSelect); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font"); - canvas_set_font(canvas, FontSecondary); + uint8_t font_x_offset = + SCREEN_WIDTH_CENTER - + (((font->charInfo[0].width + font->spacePixels) * FONT_TEST_STR_LENGTH) >> 1); + uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1); + canvas_draw_str_ex( + canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font); - const FONT_INFO* const font = available_fonts[scene_state->active_font]; - ui_control_select_render( - canvas, - 0, - 74 - scene_state->y_offset, - SCREEN_WIDTH, - font->name, - scene_state->selected_control == FontSelect); + } else if(scene_state->selected_control < AutomationSwitch) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); + canvas_set_font(canvas, FontSecondary); - uint8_t font_x_offset = - SCREEN_WIDTH_CENTER - - (((font->charInfo[0].width + font->spacePixels) * FONT_TEST_STR_LENGTH) >> 1); - uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1); - canvas_draw_str_ex( - canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font); + canvas_draw_str_aligned( + canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); + ui_control_select_render( + canvas, + 36, + 138 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + YES_NO_LIST[scene_state->notification_sound], + scene_state->selected_control == SoundSwitch); - canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, 123 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); + canvas_draw_str_aligned( + canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); + ui_control_select_render( + canvas, + 36, + 156 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + YES_NO_LIST[scene_state->notification_vibro], + scene_state->selected_control == VibroSwitch); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); + canvas_set_font(canvas, FontSecondary); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); - canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 0, 209 - scene_state->y_offset, AlignLeft, AlignTop, "Method:"); + ui_control_select_render( + canvas, + 36, + 202 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + AUTOMATION_LIST[scene_state->automation_method], + scene_state->selected_control == AutomationSwitch); - canvas_draw_str_aligned(canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); - ui_control_select_render( - canvas, - 36, - 138 - scene_state->y_offset, - SCREEN_WIDTH - 36, - YES_NO_LIST[scene_state->notification_sound], - scene_state->selected_control == SoundSwitch); + canvas_draw_str_aligned( + canvas, 0, 227 - scene_state->y_offset, AlignLeft, AlignTop, "Layout:"); - canvas_draw_str_aligned(canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); - ui_control_select_render( - canvas, - 36, - 156 - scene_state->y_offset, - SCREEN_WIDTH - 36, - YES_NO_LIST[scene_state->notification_vibro], - scene_state->selected_control == VibroSwitch); + ui_control_select_render( + canvas, + 36, + 220 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + BAD_KB_LAYOUT_LIST[scene_state->automation_kb_layout], + scene_state->selected_control == BadKeyboardLayoutSelect); - canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, 187 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 24, + 242 - scene_state->y_offset, + 48, + 13, + "Confirm", + scene_state->selected_control == ConfirmButton); + } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 0, 209 - scene_state->y_offset, AlignLeft, AlignTop, "BadUSB:"); - ui_control_select_render( - canvas, - 36, - 202 - scene_state->y_offset, - SCREEN_WIDTH - 36, - ON_OFF_LIST[scene_state->badusb_enabled], - scene_state->selected_control == BadUsbSwitch); - -#ifdef TOTP_BADBT_TYPE_ENABLED - canvas_draw_str_aligned(canvas, 0, 227 - scene_state->y_offset, AlignLeft, AlignTop, "BadBT:"); - ui_control_select_render( - canvas, - 36, - 220 - scene_state->y_offset, - SCREEN_WIDTH - 36, - ON_OFF_LIST[scene_state->badbt_enabled], - scene_state->selected_control == BadBtSwitch); -#endif - - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 24, -#ifdef TOTP_BADBT_TYPE_ENABLED - 242 - scene_state->y_offset, -#else - 229 - scene_state->y_offset, -#endif - 48, - 13, - "Confirm", - scene_state->selected_control == ConfirmButton); + ui_control_vscroll_render( + canvas, SCREEN_WIDTH - 3, 0, SCREEN_HEIGHT, scene_state->selected_control, ConfirmButton); } bool totp_scene_app_settings_handle_event( @@ -268,14 +275,21 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == VibroSwitch) { scene_state->notification_vibro = !scene_state->notification_vibro; - } else if(scene_state->selected_control == BadUsbSwitch) { - scene_state->badusb_enabled = !scene_state->badusb_enabled; + } else if(scene_state->selected_control == AutomationSwitch) { + totp_roll_value_uint8_t( + &scene_state->automation_method, + 1, + 0, + AUTOMATION_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == BadKeyboardLayoutSelect) { + totp_roll_value_uint8_t( + &scene_state->automation_kb_layout, + 1, + 0, + BAD_KB_LAYOUT_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); } -#ifdef TOTP_BADBT_TYPE_ENABLED - else if(scene_state->selected_control == BadBtSwitch) { - scene_state->badbt_enabled = !scene_state->badbt_enabled; - } -#endif break; case InputKeyLeft: if(scene_state->selected_control == HoursInput) { @@ -295,14 +309,21 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == VibroSwitch) { scene_state->notification_vibro = !scene_state->notification_vibro; - } else if(scene_state->selected_control == BadUsbSwitch) { - scene_state->badusb_enabled = !scene_state->badusb_enabled; + } else if(scene_state->selected_control == AutomationSwitch) { + totp_roll_value_uint8_t( + &scene_state->automation_method, + -1, + 0, + AUTOMATION_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == BadKeyboardLayoutSelect) { + totp_roll_value_uint8_t( + &scene_state->automation_kb_layout, + -1, + 0, + BAD_KB_LAYOUT_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); } -#ifdef TOTP_BADBT_TYPE_ENABLED - else if(scene_state->selected_control == BadBtSwitch) { - scene_state->badbt_enabled = !scene_state->badbt_enabled; - } -#endif break; case InputKeyOk: break; @@ -323,22 +344,18 @@ bool totp_scene_app_settings_handle_event( (scene_state->notification_sound ? NotificationMethodSound : NotificationMethodNone) | (scene_state->notification_vibro ? NotificationMethodVibro : NotificationMethodNone); - plugin_state->automation_method = scene_state->badusb_enabled ? AutomationMethodBadUsb : - AutomationMethodNone; -#ifdef TOTP_BADBT_TYPE_ENABLED - plugin_state->automation_method |= scene_state->badbt_enabled ? AutomationMethodBadBt : - AutomationMethodNone; -#endif - + plugin_state->automation_method = scene_state->automation_method; plugin_state->active_font_index = scene_state->active_font; + plugin_state->automation_kb_layout = scene_state->automation_kb_layout; if(!totp_config_file_update_user_settings(plugin_state)) { totp_dialogs_config_updating_error(plugin_state); return false; } -#ifdef TOTP_BADBT_TYPE_ENABLED - if(!scene_state->badbt_enabled && plugin_state->bt_type_code_worker_context != NULL) { +#ifdef TOTP_BADBT_AUTOMATION_ENABLED + if((scene_state->automation_method & AutomationMethodBadBt) == 0 && + plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); plugin_state->bt_type_code_worker_context = NULL; } diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index 23a919ed7..01a5e6cb7 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -7,10 +7,10 @@ #include "../../../services/config/config.h" #include "../../scene_director.h" #include "../../totp_scenes_enum.h" -#include "../../../services/crypto/crypto.h" +#include "../../../services/crypto/crypto_facade.h" #include "../../../types/user_pin_codes.h" -#define MAX_CODE_LENGTH TOTP_IV_SIZE +#define MAX_CODE_LENGTH CRYPTO_IV_LENGTH static const uint8_t PIN_ASTERISK_RADIUS = 3; static const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2; @@ -25,7 +25,7 @@ void totp_scene_authenticate_activate(PluginState* plugin_state) { scene_state->code_length = 0; memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); plugin_state->current_scene_state = scene_state; - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); } void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) { @@ -36,7 +36,7 @@ void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_st v_shift = -10; } - if(plugin_state->crypto_verify_data == NULL) { + if(plugin_state->crypto_settings.crypto_verify_data == NULL) { canvas_draw_str_aligned( canvas, SCREEN_WIDTH_CENTER, @@ -124,20 +124,22 @@ bool totp_scene_authenticate_handle_event( } } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) { CryptoSeedIVResult seed_result = totp_crypto_seed_iv( - plugin_state, &scene_state->code_input[0], scene_state->code_length); + &plugin_state->crypto_settings, &scene_state->code_input[0], scene_state->code_length); if(seed_result & CryptoSeedIVResultFlagSuccess && seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { totp_config_file_update_crypto_signatures(plugin_state); } - if(totp_crypto_verify_key(plugin_state)) { + if(totp_crypto_verify_key(&plugin_state->crypto_settings)) { FURI_LOG_D(LOGGING_TAG, "PIN is valid"); + totp_config_file_ensure_latest_encryption( + plugin_state, &scene_state->code_input[0], scene_state->code_length); totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); scene_state->code_length = 0; DialogMessage* message = dialog_message_alloc(); diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index e4a60b1e0..3ef578542 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -16,7 +16,7 @@ #include "../../../features_config.h" #include "../../../workers/generate_totp_code/generate_totp_code.h" #include "../../../workers/usb_type_code/usb_type_code.h" -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif @@ -34,50 +34,40 @@ typedef struct { typedef struct { char last_code[TokenDigitsCountMax + 1]; TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; - NotificationMessage const** notification_sequence_new_token; - NotificationMessage const** notification_sequence_automation; + NotificationMessage const* notification_sequence_new_token[8]; + NotificationMessage const* notification_sequence_automation[11]; FuriMutex* last_code_update_sync; TotpGenerateCodeWorkerContext* generate_code_worker_context; UiPrecalculatedDimensions ui_precalculated_dimensions; const FONT_INFO* active_font; + NotificationApp* notification_app; } SceneState; static const NotificationSequence* get_notification_sequence_new_token(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_new_token == NULL) { - uint8_t i = 0; - uint8_t length = 4; + if(scene_state->notification_sequence_new_token[0] == NULL) { + NotificationMessage const** sequence = &scene_state->notification_sequence_new_token[0]; + *(sequence++) = &message_display_backlight_on; + *(sequence++) = &message_green_255; if(plugin_state->notification_method & NotificationMethodVibro) { - length += 2; + *(sequence++) = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - length += 2; + *(sequence++) = &message_note_c5; } - scene_state->notification_sequence_new_token = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_new_token != NULL); - scene_state->notification_sequence_new_token[i++] = &message_display_backlight_on; - scene_state->notification_sequence_new_token[i++] = &message_green_255; + *(sequence++) = &message_delay_50; + if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_new_token[i++] = &message_vibro_on; + *(sequence++) = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_new_token[i++] = &message_note_c5; + *(sequence++) = &message_sound_off; } - scene_state->notification_sequence_new_token[i++] = &message_delay_50; - - if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_new_token[i++] = &message_vibro_off; - } - - if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_new_token[i++] = &message_sound_off; - } - - scene_state->notification_sequence_new_token[i++] = NULL; + *(sequence++) = NULL; } return (NotificationSequence*)scene_state->notification_sequence_new_token; @@ -85,44 +75,33 @@ static const NotificationSequence* static const NotificationSequence* get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_automation == NULL) { - uint8_t i = 0; - uint8_t length = 3; + if(scene_state->notification_sequence_automation[0] == NULL) { + NotificationMessage const** sequence = &scene_state->notification_sequence_automation[0]; + + *(sequence++) = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - length += 2; + *(sequence++) = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - length += 6; + *(sequence++) = &message_note_d5; //-V525 + *(sequence++) = &message_delay_50; + *(sequence++) = &message_note_e4; + *(sequence++) = &message_delay_50; + *(sequence++) = &message_note_f3; } - scene_state->notification_sequence_automation = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_automation != NULL); + *(sequence++) = &message_delay_50; - scene_state->notification_sequence_automation[i++] = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_automation[i++] = &message_vibro_on; + *(sequence++) = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_automation[i++] = &message_note_d5; //-V525 - scene_state->notification_sequence_automation[i++] = &message_delay_50; - scene_state->notification_sequence_automation[i++] = &message_note_e4; - scene_state->notification_sequence_automation[i++] = &message_delay_50; - scene_state->notification_sequence_automation[i++] = &message_note_f3; + *(sequence++) = &message_sound_off; } - scene_state->notification_sequence_automation[i++] = &message_delay_50; - - if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_automation[i++] = &message_vibro_off; - } - - if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_automation[i++] = &message_sound_off; - } - - scene_state->notification_sequence_automation[i++] = NULL; + *(sequence++) = NULL; } return (NotificationSequence*)scene_state->notification_sequence_automation; @@ -154,7 +133,7 @@ static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin } static void on_new_token_code_generated(bool time_left, void* context) { - const PluginState* plugin_state = context; + PluginState* const plugin_state = context; const TokenInfoIteratorContext* iterator_context = totp_config_get_token_iterator_context(plugin_state); if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { @@ -175,13 +154,16 @@ static void on_new_token_code_generated(bool time_left, void* context) { if(time_left) { notification_message( - plugin_state->notification_app, - get_notification_sequence_new_token(plugin_state, plugin_state->current_scene_state)); + scene_state->notification_app, + get_notification_sequence_new_token(plugin_state, scene_state)); } + + totp_scene_director_force_redraw(plugin_state); } static void on_code_lifetime_updated_generated(float code_lifetime_percent, void* context) { - SceneState* scene_state = context; + PluginState* const plugin_state = context; + SceneState* scene_state = plugin_state->current_scene_state; scene_state->ui_precalculated_dimensions.progress_bar_width = (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * code_lifetime_percent); scene_state->ui_precalculated_dimensions.progress_bar_x = @@ -189,6 +171,7 @@ static void on_code_lifetime_updated_generated(float code_lifetime_percent, void scene_state->ui_precalculated_dimensions.progress_bar_width) >> 1) + PROGRESS_BAR_MARGIN; + totp_scene_director_force_redraw(plugin_state); } void totp_scene_generate_token_activate(PluginState* plugin_state) { @@ -201,12 +184,18 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal); if(plugin_state->automation_method & AutomationMethodBadUsb) { scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start( - scene_state->last_code, TokenDigitsCountMax + 1, scene_state->last_code_update_sync); + scene_state->last_code, + TokenDigitsCountMax + 1, + scene_state->last_code_update_sync, + plugin_state->automation_kb_layout); } scene_state->active_font = available_fonts[plugin_state->active_font_index]; + scene_state->notification_app = furi_record_open(RECORD_NOTIFICATION); + scene_state->notification_sequence_automation[0] = NULL; + scene_state->notification_sequence_new_token[0] = NULL; -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { if(plugin_state->bt_type_code_worker_context == NULL) { @@ -216,7 +205,8 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { plugin_state->bt_type_code_worker_context, scene_state->last_code, TokenDigitsCountMax + 1, - scene_state->last_code_update_sync); + scene_state->last_code_update_sync, + plugin_state->automation_kb_layout); } #endif const TokenInfoIteratorContext* iterator_context = @@ -226,7 +216,7 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { totp_token_info_iterator_get_current_token(iterator_context), scene_state->last_code_update_sync, plugin_state->timezone_offset, - plugin_state->iv); + &plugin_state->crypto_settings); totp_generate_code_worker_set_code_generated_handler( scene_state->generate_code_worker_context, &on_new_token_code_generated, plugin_state); @@ -234,7 +224,7 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { totp_generate_code_worker_set_lifetime_changed_handler( scene_state->generate_code_worker_context, &on_code_lifetime_updated_generated, - scene_state); + plugin_state); update_totp_params( plugin_state, totp_token_info_iterator_get_current_token_index(iterator_context)); @@ -298,11 +288,10 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ canvas, SCREEN_WIDTH - 8, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } -#ifdef TOTP_AUTOMATION_ICONS_ENABLED if(plugin_state->automation_method & AutomationMethodBadUsb) { canvas_draw_icon( canvas, -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED SCREEN_WIDTH_CENTER - (plugin_state->automation_method & AutomationMethodBadBt ? 33 : 15), #else @@ -313,7 +302,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ &I_hid_usb_31x9); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt && plugin_state->bt_type_code_worker_context != NULL && totp_bt_type_code_worker_is_advertising(plugin_state->bt_type_code_worker_context)) { @@ -325,7 +314,6 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ &I_hid_ble_31x9); } #endif -#endif } bool totp_scene_generate_token_handle_event( @@ -351,11 +339,11 @@ bool totp_scene_generate_token_handle_event( TotpUsbTypeCodeWorkerEventType, totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( - plugin_state->notification_app, + scene_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); return true; } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED else if( event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { @@ -367,7 +355,7 @@ bool totp_scene_generate_token_handle_event( TotpBtTypeCodeWorkerEventType, totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( - plugin_state->notification_app, + scene_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); return true; } @@ -428,23 +416,17 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { totp_generate_code_worker_stop(scene_state->generate_code_worker_context); + furi_record_close(RECORD_NOTIFICATION); + if(plugin_state->automation_method & AutomationMethodBadUsb) { totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { totp_bt_type_code_worker_stop(plugin_state->bt_type_code_worker_context); } #endif - if(scene_state->notification_sequence_new_token != NULL) { - free(scene_state->notification_sequence_new_token); - } - - if(scene_state->notification_sequence_automation != NULL) { - free(scene_state->notification_sequence_automation); - } - furi_mutex_free(scene_state->last_code_update_sync); free(scene_state); diff --git a/applications/external/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c index c6ad3e1fe..438556813 100644 --- a/applications/external/totp/ui/ui_controls.c +++ b/applications/external/totp/ui/ui_controls.c @@ -113,3 +113,27 @@ void ui_control_button_render( canvas_set_color(canvas, ColorBlack); } } + +void ui_control_vscroll_render( + Canvas* const canvas, + uint8_t x, + uint8_t y, + uint8_t height, + uint8_t position, + uint8_t max_position) { + canvas_draw_line(canvas, x, y, x, y + height); + uint8_t block_height = height / MIN(10, max_position); + uint8_t block_position_y = + height * ((float)position / (float)max_position) - (block_height >> 1); + uint8_t block_position_y_abs = y + block_position_y; + if(block_position_y_abs + block_height > height) { + block_position_y_abs = height - block_height; + } + + canvas_draw_box( + canvas, + x - (UI_CONTROL_VSCROLL_WIDTH >> 1), + block_position_y_abs, + UI_CONTROL_VSCROLL_WIDTH, + block_height); +} diff --git a/applications/external/totp/ui/ui_controls.h b/applications/external/totp/ui/ui_controls.h index b97006a03..ccee4edfc 100644 --- a/applications/external/totp/ui/ui_controls.h +++ b/applications/external/totp/ui/ui_controls.h @@ -3,6 +3,8 @@ #include #include +#define UI_CONTROL_VSCROLL_WIDTH (3) + /** * @brief Renders TextBox control * @param canvas canvas to render control at @@ -51,3 +53,20 @@ void ui_control_select_render( uint8_t width, const char* text, bool is_selected); + +/** + * @brief Renders vertical scroll bar + * @param canvas canvas to render control at + * @param x horizontal position of a control to be rendered at + * @param y vertical position of a control to be rendered at + * @param height control height + * @param position current position + * @param max_position maximal position + */ +void ui_control_vscroll_render( + Canvas* const canvas, + uint8_t x, + uint8_t y, + uint8_t height, + uint8_t position, + uint8_t max_position); diff --git a/applications/external/totp/version.h b/applications/external/totp/version.h new file mode 100644 index 000000000..e6925b600 --- /dev/null +++ b/applications/external/totp/version.h @@ -0,0 +1,5 @@ +#pragma once + +#define TOTP_APP_VERSION_MAJOR (3) +#define TOTP_APP_VERSION_MINOR (2) +#define TOTP_APP_VERSION_PATCH (0) \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index 0f8ad22ff..f249c4278 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -31,6 +31,7 @@ struct TotpBtTypeCodeWorkerContext { char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; #endif + AutomationKeyboardLayout keyboard_layout; }; static inline bool totp_type_code_worker_stop_requested() { @@ -71,7 +72,8 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context &furi_hal_bt_hid_kb_release, context->code_buffer, context->code_buffer_size, - context->flags); + context->flags, + context->keyboard_layout); furi_mutex_release(context->code_buffer_sync); } } @@ -117,11 +119,13 @@ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync) { + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout) { furi_check(context != NULL); context->code_buffer = code_buffer; context->code_buffer_size = code_buffer_size; context->code_buffer_sync = code_buffer_sync; + context->keyboard_layout = keyboard_layout; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPBtHidWorker"); furi_thread_set_stack_size(context->thread, 1024); diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index d0de55124..36b31936e 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -3,8 +3,10 @@ #include #include #include +#include "../../types/automation_kb_layout.h" +#include "../../services/config/constants.h" -#define TOTP_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") +#define TOTP_BT_KEYS_STORAGE_PATH EXT_PATH("apps_data/totp/.bt_hid.keys") typedef uint8_t TotpBtTypeCodeWorkerEvent; @@ -49,12 +51,14 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); * @param code_buffer code buffer to be used to automate * @param code_buffer_size code buffer size * @param code_buffer_sync code buffer synchronization primitive + * @param keyboard_layout keyboard layout to be used */ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync); + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout); /** * @brief Stops bluetooth token input automation worker diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c index 74482517f..20a7bb54c 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c @@ -1,6 +1,7 @@ #include "generate_totp_code.h" #include -#include "../../services/crypto/crypto.h" +#include +#include "../../services/crypto/crypto_facade.h" #include "../../services/totp/totp.h" #include "../../services/convert/convert.h" #include @@ -14,7 +15,7 @@ struct TotpGenerateCodeWorkerContext { FuriMutex* code_buffer_sync; const TokenInfo* token_info; float timezone_offset; - uint8_t* iv; + const CryptoSettings* crypto_settings; TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; void* on_new_code_generated_handler_context; TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; @@ -69,7 +70,7 @@ static void generate_totp_code( if(token_info->token != NULL && token_info->token_length > 0) { size_t key_length; uint8_t* key = totp_crypto_decrypt( - token_info->token, token_info->token_length, context->iv, &key_length); + token_info->token, token_info->token_length, context->crypto_settings, &key_length); int_token_to_str( totp_at( @@ -147,14 +148,14 @@ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, - uint8_t* iv) { + const CryptoSettings* crypto_settings) { TotpGenerateCodeWorkerContext* context = malloc(sizeof(TotpGenerateCodeWorkerContext)); furi_check(context != NULL); context->code_buffer = code_buffer; context->token_info = token_info; context->code_buffer_sync = code_buffer_sync; context->timezone_offset = timezone_offset; - context->iv = iv; + context->crypto_settings = crypto_settings; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPGenerateWorker"); furi_thread_set_stack_size(context->thread, 2048); diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h index f351ffa68..eb234313e 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h @@ -38,7 +38,7 @@ enum TotGenerateCodeWorkerEvents { * @param token_info token info to be used to generate code * @param code_buffer_sync code buffer synchronization primitive * @param timezone_offset timezone offset to be used to generate code - * @param iv initialization vector (IV) to be used to decrypt token secret + * @param crypto_settings crypto settings * @return worker context */ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( @@ -46,7 +46,7 @@ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, - uint8_t* iv); + const CryptoSettings* crypto_settings); /** * @brief Stops generate code worker diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index 82a5a028e..122c0b2a5 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -3,7 +3,9 @@ #include #include "../../services/convert/convert.h" -static const uint8_t hid_number_keys[] = { +#define HID_KEYS_MAP_LENGTH (36) + +static const uint8_t hid_qwerty_keys_map[HID_KEYS_MAP_LENGTH] = { HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, @@ -13,6 +15,16 @@ static const uint8_t hid_number_keys[] = { HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y, HID_KEYBOARD_Z}; +static const uint8_t hid_azerty_keys_map[HID_KEYS_MAP_LENGTH] = { + HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, + HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, + HID_KEYBOARD_Q, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, + HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J, + HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_SEMICOLON, HID_KEYBOARD_N, HID_KEYBOARD_O, + HID_KEYBOARD_P, HID_KEYBOARD_A, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T, + HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_Z, HID_KEYBOARD_X, HID_KEYBOARD_Y, + HID_KEYBOARD_W}; + static uint32_t get_keystroke_delay(TokenAutomationFeature features) { if(features & TokenAutomationFeatureTypeSlower) { return 100; @@ -44,21 +56,38 @@ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_release_fn, const char* code_buffer, uint8_t code_buffer_size, - TokenAutomationFeature features) { + TokenAutomationFeature features, + AutomationKeyboardLayout keyboard_layout) { furi_delay_ms(500); uint8_t i = 0; char cb_char; + const uint8_t* keyboard_layout_dict; + switch(keyboard_layout) { + case AutomationKeyboardLayoutQWERTY: + keyboard_layout_dict = &hid_qwerty_keys_map[0]; + break; + case AutomationKeyboardLayoutAZERTY: + keyboard_layout_dict = &hid_azerty_keys_map[0]; + break; + + default: + return; + } + while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) { uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char); if(char_index > 9) { char_index = cb_char - 'A' + 10; } - if(char_index >= sizeof(hid_number_keys)) break; + if(char_index >= HID_KEYS_MAP_LENGTH) break; - uint16_t hid_kb_key = hid_number_keys[char_index]; - if(char_index > 9) { + uint16_t hid_kb_key = keyboard_layout_dict[char_index]; + + // For non-AZERTY press shift for all non-digit chars + // For AZERTY press shift for all characters + if(char_index > 9 || keyboard_layout == AutomationKeyboardLayoutAZERTY) { hid_kb_key |= KEY_MOD_LEFT_SHIFT; } diff --git a/applications/external/totp/workers/type_code_common.h b/applications/external/totp/workers/type_code_common.h index db357329a..81b273c36 100644 --- a/applications/external/totp/workers/type_code_common.h +++ b/applications/external/totp/workers/type_code_common.h @@ -1,6 +1,7 @@ #pragma once #include #include "../types/token_info.h" +#include "../types/automation_kb_layout.h" typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); @@ -11,10 +12,12 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); * @param code_buffer code buffer to be typed * @param code_buffer_size code buffer size * @param features automation features + * @param keyboard_layout keyboard layout to be used */ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, const char* code_buffer, uint8_t code_buffer_size, - TokenAutomationFeature features); \ No newline at end of file + TokenAutomationFeature features, + AutomationKeyboardLayout keyboard_layout); \ No newline at end of file diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index a391bdf82..4e3259424 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -15,6 +15,7 @@ struct TotpUsbTypeCodeWorkerContext { FuriThread* thread; FuriMutex* code_buffer_sync; FuriHalUsbInterface* usb_mode_prev; + AutomationKeyboardLayout keyboard_layout; }; static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { @@ -45,7 +46,8 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex &furi_hal_hid_kb_release, context->code_buffer, context->code_buffer_size, - context->flags); + context->flags, + context->keyboard_layout); furi_mutex_release(context->code_buffer_sync); furi_delay_ms(100); @@ -83,7 +85,8 @@ static int32_t totp_type_code_worker_callback(void* context) { TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync) { + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout) { TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext)); furi_check(context != NULL); context->code_buffer = code_buffer; @@ -91,6 +94,7 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); context->usb_mode_prev = NULL; + context->keyboard_layout = keyboard_layout; furi_thread_set_name(context->thread, "TOTPUsbHidWorker"); furi_thread_set_stack_size(context->thread, 1024); furi_thread_set_context(context->thread, context); diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h index 0a700e7fe..d2d1bdf82 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.h +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -3,6 +3,7 @@ #include #include #include +#include "../../types/automation_kb_layout.h" typedef uint8_t TotpUsbTypeCodeWorkerEvent; @@ -34,12 +35,14 @@ enum TotpUsbTypeCodeWorkerEvents { * @param code_buffer code buffer to be used to automate * @param code_buffer_size code buffer size * @param code_buffer_sync code buffer synchronization primitive + * @param keyboard_layout keyboard layout to be used * @return worker context */ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync); + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout); /** * @brief Stops USB token input automation worker diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index d4bfde99f..f7f4779ca 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -171,6 +171,10 @@ static Infrared* infrared_alloc() { view_dispatcher_add_view( view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); + infrared->move_view = infrared_move_view_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewMove, infrared_move_view_get_view(infrared->move_view)); + if(app_state->is_debug_enabled) { infrared->debug_view = infrared_debug_view_alloc(); view_dispatcher_add_view( @@ -218,6 +222,9 @@ static void infrared_free(Infrared* infrared) { view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); view_stack_free(infrared->view_stack); + view_dispatcher_remove_view(view_dispatcher, InfraredViewMove); + infrared_move_view_free(infrared->move_view); + if(app_state->is_debug_enabled) { view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); infrared_debug_view_free(infrared->debug_view); diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 089c2c7f8..4e69b886e 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -31,6 +31,7 @@ #include "scenes/infrared_scene.h" #include "views/infrared_progress_view.h" #include "views/infrared_debug_view.h" +#include "views/infrared_move_view.h" #include "rpc/rpc_app.h" @@ -61,8 +62,6 @@ typedef enum { InfraredEditModeNone, InfraredEditModeRename, InfraredEditModeDelete, - InfraredEditModeMove, - InfraredEditModeMoveSelectDest } InfraredEditMode; typedef struct { @@ -98,6 +97,7 @@ struct Infrared { ViewStack* view_stack; InfraredDebugView* debug_view; + InfraredMoveView* move_view; ButtonPanel* button_panel; Loading* loading; @@ -119,6 +119,7 @@ typedef enum { InfraredViewPopup, InfraredViewStack, InfraredViewDebugView, + InfraredViewMove, } InfraredView; typedef enum { diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index a04a338ba..70d1b59ef 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -108,19 +108,13 @@ bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { return infrared_remote_store(remote); } -bool infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) { +void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) { furi_assert(index_orig < InfraredButtonArray_size(remote->buttons)); - furi_assert(index_dest <= InfraredButtonArray_size(remote->buttons)); - if(index_orig == index_dest) { - return true; - } + furi_assert(index_dest < InfraredButtonArray_size(remote->buttons)); + InfraredRemoteButton* button; InfraredButtonArray_pop_at(&button, remote->buttons, index_orig); - if(index_orig > index_dest) - InfraredButtonArray_push_at(remote->buttons, index_dest, button); - else - InfraredButtonArray_push_at(remote->buttons, index_dest - 1, button); - return infrared_remote_store(remote); + InfraredButtonArray_push_at(remote->buttons, index_dest, button); } bool infrared_remote_store(InfraredRemote* remote) { diff --git a/applications/main/infrared/infrared_remote.h b/applications/main/infrared/infrared_remote.h index 2640149a4..47aa77e2e 100644 --- a/applications/main/infrared/infrared_remote.h +++ b/applications/main/infrared/infrared_remote.h @@ -23,7 +23,7 @@ bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* nam bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); -bool infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest); +void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest); bool infrared_remote_store(InfraredRemote* remote); bool infrared_remote_load(InfraredRemote* remote, FuriString* path); diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 71df8eff9..64f8807f5 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -8,7 +8,6 @@ ADD_SCENE(infrared, edit_button_select, EditButtonSelect) ADD_SCENE(infrared, edit_rename, EditRename) ADD_SCENE(infrared, edit_rename_done, EditRenameDone) ADD_SCENE(infrared, edit_move, EditMove) -ADD_SCENE(infrared, edit_move_done, EditMoveDone) ADD_SCENE(infrared, learn, Learn) ADD_SCENE(infrared, learn_done, LearnDone) ADD_SCENE(infrared, learn_enter_name, LearnEnterName) diff --git a/applications/main/infrared/scenes/infrared_scene_edit.c b/applications/main/infrared/scenes/infrared_scene_edit.c index 79de04bda..02bba7a3f 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit.c +++ b/applications/main/infrared/scenes/infrared_scene_edit.c @@ -82,9 +82,7 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); consumed = true; } else if(submenu_index == SubmenuIndexMoveButton) { - infrared->app_state.edit_target = InfraredEditTargetButton; - infrared->app_state.edit_mode = InfraredEditModeMove; - scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); + scene_manager_next_scene(scene_manager, InfraredSceneEditMove); consumed = true; } else if(submenu_index == SubmenuIndexDeleteButton) { infrared->app_state.edit_target = InfraredEditTargetButton; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_button_select.c b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c index 7056a2053..5f5a1d8fa 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_button_select.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c @@ -11,23 +11,9 @@ void infrared_scene_edit_button_select_on_enter(void* context) { InfraredRemote* remote = infrared->remote; InfraredAppState* app_state = &infrared->app_state; - const char* header = NULL; - switch(infrared->app_state.edit_mode) { - case InfraredEditModeRename: - header = "Rename Button:"; - break; - case InfraredEditModeDelete: - header = "Delete Button:"; - break; - case InfraredEditModeMove: - header = "Select Button to Move:"; - break; - case InfraredEditModeMoveSelectDest: - case InfraredEditModeNone: - default: - header = "Move Button Before:"; - break; - } + const char* header = infrared->app_state.edit_mode == InfraredEditModeRename ? + "Rename Button:" : + "Delete Button:"; submenu_set_header(submenu, header); const size_t button_count = infrared_remote_get_button_count(remote); @@ -40,14 +26,6 @@ void infrared_scene_edit_button_select_on_enter(void* context) { infrared_scene_edit_button_select_submenu_callback, context); } - if(infrared->app_state.edit_mode == InfraredEditModeMoveSelectDest) { - submenu_add_item( - submenu, - "-- Move to the end --", - button_count, - infrared_scene_edit_button_select_submenu_callback, - context); - } if(button_count && app_state->current_button_index != InfraredButtonIndexNone) { submenu_set_selected_item(submenu, app_state->current_button_index); app_state->current_button_index = InfraredButtonIndexNone; @@ -69,12 +47,6 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent scene_manager_next_scene(scene_manager, InfraredSceneEditRename); } else if(edit_mode == InfraredEditModeDelete) { scene_manager_next_scene(scene_manager, InfraredSceneEditDelete); - } else if(edit_mode == InfraredEditModeMove) { - app_state->current_button_index_move_orig = event.event; - app_state->edit_mode = InfraredEditModeMoveSelectDest; - scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); - } else if(edit_mode == InfraredEditModeMoveSelectDest) { - scene_manager_next_scene(scene_manager, InfraredSceneEditMove); } else { furi_assert(0); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 69c7ec41d..370c352db 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -1,103 +1,44 @@ #include "../infrared_i.h" -static void infrared_scene_edit_move_dialog_result_callback(DialogExResult result, void* context) { - Infrared* infrared = context; - view_dispatcher_send_custom_event(infrared->view_dispatcher, result); +static void infrared_scene_move_button(uint32_t index_old, uint32_t index_new, void* context) { + InfraredRemote* remote = context; + furi_assert(remote); + infrared_remote_move_button(remote, index_old, index_new); +} + +static const char* infrared_scene_get_btn_name(uint32_t index, void* context) { + InfraredRemote* remote = context; + furi_assert(remote); + InfraredRemoteButton* button = infrared_remote_get_button(remote, index); + return (infrared_remote_button_get_name(button)); } void infrared_scene_edit_move_on_enter(void* context) { Infrared* infrared = context; - DialogEx* dialog_ex = infrared->dialog_ex; InfraredRemote* remote = infrared->remote; - const InfraredEditTarget edit_target = infrared->app_state.edit_target; - if(edit_target == InfraredEditTargetButton) { - int32_t current_button_index = infrared->app_state.current_button_index_move_orig; - furi_assert(current_button_index != InfraredButtonIndexNone); + infrared_move_view_set_callback(infrared->move_view, infrared_scene_move_button); - dialog_ex_set_header(dialog_ex, "Move Button?", 64, 0, AlignCenter, AlignTop); - InfraredRemoteButton* current_button = - infrared_remote_get_button(remote, current_button_index); - InfraredSignal* signal = infrared_remote_button_get_signal(current_button); + uint32_t btn_count = infrared_remote_get_button_count(remote); + infrared_move_view_list_init( + infrared->move_view, btn_count, infrared_scene_get_btn_name, remote); + infrared_move_view_list_update(infrared->move_view); - if(infrared_signal_is_raw(signal)) { - const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); - infrared_text_store_set( - infrared, - 0, - "%s\nRAW\n%ld samples", - infrared_remote_button_get_name(current_button), - raw->timings_size); - - } else { - const InfraredMessage* message = infrared_signal_get_message(signal); - infrared_text_store_set( - infrared, - 0, - "%s\n%s\nA=0x%0*lX C=0x%0*lX", - infrared_remote_button_get_name(current_button), - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command); - } - } else { - furi_assert(0); - } - - dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 0, 0, NULL); - dialog_ex_set_left_button_text(dialog_ex, "Cancel"); - dialog_ex_set_right_button_text(dialog_ex, "Move"); - dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_move_dialog_result_callback); - dialog_ex_set_context(dialog_ex, context); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove); } bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - consumed = true; - } else if(event.event == DialogExResultRight) { - bool success = false; - InfraredRemote* remote = infrared->remote; - InfraredAppState* app_state = &infrared->app_state; - const InfraredEditTarget edit_target = app_state->edit_target; - - if(edit_target == InfraredEditTargetButton) { - furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_move_button( - remote, - app_state->current_button_index_move_orig, - app_state->current_button_index); - app_state->current_button_index_move_orig = InfraredButtonIndexNone; - app_state->current_button_index = InfraredButtonIndexNone; - } else { - furi_assert(0); - } - - if(success) { - scene_manager_next_scene(scene_manager, InfraredSceneEditMoveDone); - } else { - const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; - scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, COUNT_OF(possible_scenes)); - } - consumed = true; - } - } + UNUSED(event); + UNUSED(infrared); return consumed; } void infrared_scene_edit_move_on_exit(void* context) { Infrared* infrared = context; - UNUSED(infrared); + InfraredRemote* remote = infrared->remote; + infrared_remote_store(remote); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move_done.c b/applications/main/infrared/scenes/infrared_scene_edit_move_done.c deleted file mode 100644 index 9f9b4b80d..000000000 --- a/applications/main/infrared/scenes/infrared_scene_edit_move_done.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "../infrared_i.h" - -void infrared_scene_edit_move_done_on_enter(void* context) { - Infrared* infrared = context; - Popup* popup = infrared->popup; - - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Moved", 83, 19, AlignLeft, AlignBottom); - - popup_set_callback(popup, infrared_popup_closed_callback); - popup_set_context(popup, context); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); -} - -bool infrared_scene_edit_move_done_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InfraredCustomEventTypePopupClosed) { - const InfraredEditTarget edit_target = infrared->app_state.edit_target; - if(edit_target == InfraredEditTargetButton) { - scene_manager_search_and_switch_to_previous_scene( - scene_manager, InfraredSceneRemote); - } else if(edit_target == InfraredEditTargetRemote) { - const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList}; - if(!scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, COUNT_OF(possible_scenes))) { - view_dispatcher_stop(infrared->view_dispatcher); - } - } else { - furi_assert(0); - } - consumed = true; - } - } - - return consumed; -} - -void infrared_scene_edit_move_done_on_exit(void* context) { - Infrared* infrared = context; - UNUSED(infrared); -} diff --git a/applications/main/infrared/views/infrared_move_view.c b/applications/main/infrared/views/infrared_move_view.c new file mode 100644 index 000000000..d838a5f82 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.c @@ -0,0 +1,215 @@ +#include "infrared_move_view.h" + +#include +#include + +#include +#include + +#define LIST_ITEMS 4U +#define LIST_LINE_H 13U +#define HEADER_H 12U +#define MOVE_X_OFFSET 5U + +struct InfraredMoveView { + View* view; + InfraredMoveCallback move_cb; + void* cb_context; +}; + +typedef struct { + const char** btn_names; + uint32_t btn_number; + int32_t list_offset; + int32_t item_idx; + bool is_moving; + + InfraredMoveGetItemCallback get_item_cb; +} InfraredMoveViewModel; + +static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) { + InfraredMoveViewModel* model = _model; + + UNUSED(model); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move"); + + bool show_scrollbar = model->btn_number > LIST_ITEMS; + + canvas_set_font(canvas, FontSecondary); + + for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) { + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u); + uint8_t x_offset = (model->is_moving && model->item_idx == idx) ? MOVE_X_OFFSET : 0; + uint8_t y_offset = HEADER_H + i * LIST_LINE_H; + uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1); + + canvas_set_color(canvas, ColorBlack); + if(model->item_idx == idx) { + canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, x_offset, y_offset); + canvas_draw_dot(canvas, x_offset + 1, y_offset); + canvas_draw_dot(canvas, x_offset, y_offset + 1); + canvas_draw_dot(canvas, x_offset, y_offset + LIST_LINE_H - 1); + canvas_draw_dot(canvas, box_end_x - 1, y_offset); + canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1); + } + canvas_draw_str_aligned( + canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]); + } + + if(show_scrollbar) { + elements_scrollbar_pos( + canvas, + canvas_width(canvas), + HEADER_H, + canvas_height(canvas) - HEADER_H, + model->item_idx, + model->btn_number); + } +} + +static void update_list_offset(InfraredMoveViewModel* model) { + int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number; + + if((model->btn_number > (LIST_ITEMS - 1)) && + (model->item_idx >= ((int32_t)model->btn_number - 1))) { + model->list_offset = model->item_idx - (LIST_ITEMS - 1); + } else if(model->list_offset < model->item_idx - bounds) { + model->list_offset = CLAMP( + model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->btn_number - bounds, 0); + } else if(model->list_offset > model->item_idx - bounds) { + model->list_offset = CLAMP(model->item_idx - 1, (int32_t)model->btn_number - bounds, 0); + } +} + +static bool infrared_move_view_input_callback(InputEvent* event, void* context) { + InfraredMoveView* move_view = context; + + bool consumed = false; + + if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) && + ((event->key == InputKeyUp) || (event->key == InputKeyDown))) { + bool is_moving = false; + uint32_t index_old = 0; + uint32_t index_new = 0; + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + is_moving = model->is_moving; + index_old = model->item_idx; + if(event->key == InputKeyUp) { + if(model->item_idx <= 0) { + model->item_idx = model->btn_number; + } + model->item_idx--; + } else if(event->key == InputKeyDown) { + model->item_idx++; + if(model->item_idx >= (int32_t)(model->btn_number)) { + model->item_idx = 0; + } + } + index_new = model->item_idx; + update_list_offset(model); + }, + !is_moving); + if((is_moving) && (move_view->move_cb)) { + move_view->move_cb(index_old, index_new, move_view->cb_context); + infrared_move_view_list_update(move_view); + } + consumed = true; + } + + if((event->key == InputKeyOk) && (event->type == InputTypeShort)) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { model->is_moving = !(model->is_moving); }, + true); + consumed = true; + } + return consumed; +} + +static void infrared_move_view_on_exit(void* context) { + furi_assert(context); + InfraredMoveView* move_view = context; + + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + if(model->btn_names) { + free(model->btn_names); + model->btn_names = NULL; + } + model->btn_number = 0; + model->get_item_cb = NULL; + }, + false); + move_view->cb_context = NULL; +} + +void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback) { + furi_assert(move_view); + move_view->move_cb = callback; +} + +void infrared_move_view_list_init( + InfraredMoveView* move_view, + uint32_t item_count, + InfraredMoveGetItemCallback load_cb, + void* context) { + furi_assert(move_view); + move_view->cb_context = context; + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + furi_assert(model->btn_names == NULL); + model->btn_names = malloc(sizeof(char*) * item_count); + model->btn_number = item_count; + model->get_item_cb = load_cb; + }, + false); +} + +void infrared_move_view_list_update(InfraredMoveView* move_view) { + furi_assert(move_view); + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + for(uint32_t i = 0; i < model->btn_number; i++) { + if(!model->get_item_cb) break; + model->btn_names[i] = model->get_item_cb(i, move_view->cb_context); + } + }, + true); +} + +InfraredMoveView* infrared_move_view_alloc(void) { + InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView)); + move_view->view = view_alloc(); + view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel)); + view_set_draw_callback(move_view->view, infrared_move_view_draw_callback); + view_set_input_callback(move_view->view, infrared_move_view_input_callback); + view_set_exit_callback(move_view->view, infrared_move_view_on_exit); + view_set_context(move_view->view, move_view); + return move_view; +} + +void infrared_move_view_free(InfraredMoveView* move_view) { + view_free(move_view->view); + free(move_view); +} + +View* infrared_move_view_get_view(InfraredMoveView* move_view) { + return move_view->view; +} diff --git a/applications/main/infrared/views/infrared_move_view.h b/applications/main/infrared/views/infrared_move_view.h new file mode 100644 index 000000000..b9b0cd864 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +typedef struct InfraredMoveView InfraredMoveView; + +typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context); + +typedef const char* (*InfraredMoveGetItemCallback)(uint32_t index, void* context); + +InfraredMoveView* infrared_move_view_alloc(void); + +void infrared_move_view_free(InfraredMoveView* debug_view); + +View* infrared_move_view_get_view(InfraredMoveView* debug_view); + +void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback); + +void infrared_move_view_list_init( + InfraredMoveView* move_view, + uint32_t item_count, + InfraredMoveGetItemCallback load_cb, + void* context); + +void infrared_move_view_list_update(InfraredMoveView* move_view); \ No newline at end of file diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 96f43e1d7..eb8938974 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -9,7 +9,7 @@ #include "storage/filesystem_api_defines.h" #include "storage/storage.h" #include -#include +#include #include #include @@ -271,6 +271,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex }; PB_Storage_ListResponse* list = &response.content.storage_list_response; + bool include_md5 = request->content.storage_list_request.include_md5; + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + bool finish = false; int i = 0; @@ -296,6 +301,21 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex list->file[i].size = fileinfo.size; list->file[i].data = NULL; list->file[i].name = name; + + if(include_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf( //-V576 + md5_path, + "%s/%s", + request->content.storage_list_request.path, + name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } else { free(name); @@ -310,8 +330,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex response.has_next = false; rpc_send_and_release(session, &response); + furi_string_free(md5); + furi_string_free(md5_path); storage_dir_close(dir); storage_file_free(dir); + storage_file_free(file); furi_record_close(RECORD_STORAGE); } @@ -569,23 +592,10 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t size_to_read = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(size_to_read); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, size_to_read); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - + if(md5_string_calc_file(file, filename, md5, &file_error)) { PB_Main response = { .command_id = request->command_id, .command_status = PB_CommandStatus_OK, @@ -595,21 +605,15 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); - (void)md5sum_size; - furi_assert(hash_size <= ((md5sum_size - 1) / 2)); //-V547 - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); - free(hash); - free(data); - storage_file_close(file); rpc_send_and_release(session, &response); } else { rpc_send_and_release_empty( - session, request->command_id, rpc_system_storage_get_file_error(file)); + session, request->command_id, rpc_system_storage_get_error(file_error)); } + furi_string_free(md5); storage_file_free(file); furi_record_close(RECORD_STORAGE); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index bf1478319..e69a189e0 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -505,34 +505,16 @@ static void storage_cli_md5(Cli* cli, FuriString* path) { UNUSED(cli); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t buffer_size = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(buffer_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, buffer_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - printf("%02x", hash[i]); - } - printf("\r\n"); - - free(hash); - free(data); + if(md5_string_calc_file(file, furi_string_get_cstr(path), md5, &file_error)) { + printf("%s\r\n", furi_string_get_cstr(md5)); } else { - storage_cli_print_error(storage_file_get_error(file)); + storage_cli_print_error(file_error); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); diff --git a/assets/protobuf b/assets/protobuf index 08a907d95..7e011a958 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 08a907d95733600becc41c0602ef5ee4c4baf782 +Subproject commit 7e011a95863716e72e7c6b5d552bca241d688304 diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 1fe72fde7..b1198bf43 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -78,6 +78,7 @@ duty_cycle: 0.330000 data: 8972 4491 592 1651 592 1655 598 532 599 535 597 542 600 541 601 544 598 1656 597 526 595 1652 591 1658 595 539 593 545 597 545 597 546 596 537 594 529 592 535 596 1653 600 534 597 541 601 539 592 552 600 533 598 525 596 530 591 538 593 539 592 1665 598 1662 591 1673 601 533 598 526 595 533 598 532 600 534 597 540 591 548 594 550 592 542 600 523 598 528 593 536 595 537 594 543 599 542 600 543 599 517 594 7937 593 531 601 526 595 535 597 537 594 542 600 541 601 543 599 1654 599 523 598 528 593 536 596 538 594 542 600 541 590 552 600 532 599 524 597 528 593 536 595 537 595 541 601 539 593 551 591 542 600 522 599 527 594 536 595 537 594 543 599 540 591 552 600 532 600 523 598 527 594 535 596 537 595 542 600 540 591 552 600 532 600 523 598 528 593 536 595 538 593 543 599 541 601 543 599 535 596 527 594 532 600 531 601 534 597 540 592 549 593 552 600 534 597 525 596 529 592 1655 598 534 597 1656 597 1661 592 1671 592 1644 599 7934 596 529 592 535 597 535 597 538 593 544 598 543 599 545 597 538 593 1650 593 535 596 534 597 536 595 540 591 547 595 547 595 536 595 526 595 529 592 536 595 535 596 539 593 546 596 547 595 538 593 528 593 531 601 529 592 541 601 536 596 545 597 548 594 540 592 532 600 526 595 535 596 1656 597 541 601 540 592 553 599 534 597 526 595 532 599 531 600 533 598 539 593 548 594 552 600 535 596 1647 596 531 590 538 593 1656 597 538 594 545 597 545 597 518 593 # # Model: Daichi DA25AVQS1-W +# Compatible Brands: Gree, Tosot name: Dh type: raw frequency: 38000 @@ -100,7 +101,7 @@ name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 148 110377 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 +data: 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 # name: Heat_lo type: raw @@ -225,13 +226,13 @@ name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 +data: 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 # name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 +data: 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 # name: Off type: raw @@ -256,7 +257,7 @@ name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 +data: 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 # name: Off type: raw @@ -511,80 +512,42 @@ frequency: 38000 duty_cycle: 0.330000 data: 6059 7355 592 1633 592 1633 592 1633 592 1633 592 1633 592 1633 592 1633 618 1608 617 489 617 490 616 490 616 491 590 517 589 517 614 492 590 517 590 1636 589 1636 590 1636 590 1636 589 1636 590 1636 590 1636 590 1636 590 517 589 517 589 517 589 517 590 517 589 517 589 517 589 517 589 517 589 1636 614 492 590 1636 590 1636 590 1636 589 1636 590 1636 589 1637 589 517 589 1636 590 517 589 517 589 517 614 492 590 517 589 1636 590 517 589 1636 590 517 589 517 589 517 589 517 590 1636 590 517 589 1636 590 517 589 1636 589 1636 590 1636 590 1637 589 517 589 517 589 1637 588 1637 589 517 589 1637 589 1636 590 517 589 1636 590 1636 590 517 589 517 589 1637 589 517 589 517 589 1637 589 517 589 518 588 1637 589 518 588 1637 589 517 590 1637 588 518 588 518 588 1637 589 518 588 1637 589 518 588 1637 589 518 588 1637 589 1637 589 7357 589 # -# Model: Tosot T24H-ILF/I/T24H-ILU/O -# Compatible Brands: Gree -name: Off -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9072 4445 602 1586 603 477 601 478 600 479 599 480 598 481 598 482 597 482 572 1617 597 1593 572 1617 597 483 572 507 572 507 572 507 572 507 572 507 597 482 597 482 572 507 597 482 572 1618 571 507 572 507 572 507 572 507 572 507 572 507 572 1618 571 507 572 1618 572 507 572 507 572 1618 572 507 652 20153 572 507 597 482 572 507 572 507 597 482 597 482 572 507 596 483 572 507 572 507 572 507 572 507 572 507 572 1618 596 483 572 507 596 483 595 484 572 507 597 482 595 484 597 482 597 482 572 507 572 507 597 482 572 507 597 482 572 507 597 483 571 1618 597 482 675 40391 9179 4421 599 1590 599 481 597 482 597 482 597 481 598 482 597 482 597 482 597 1592 597 1592 598 1592 597 482 597 482 597 482 597 482 597 482 597 482 597 482 597 482 597 482 572 507 597 1593 597 482 572 507 572 507 572 507 572 507 572 508 571 1618 597 1593 596 1593 572 507 572 507 572 1618 597 482 678 20152 597 483 596 482 597 482 597 482 597 482 597 482 572 507 597 482 597 482 597 482 572 507 597 482 597 482 597 482 597 482 597 482 572 507 597 482 597 482 572 507 597 482 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 596 482 572 507 572 -# -name: Cool_hi -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9049 4446 575 1613 577 503 601 478 601 1590 599 480 598 482 572 507 572 507 572 508 571 507 572 507 572 507 572 508 571 508 571 507 573 507 572 508 571 508 572 507 572 508 571 507 572 1618 572 508 572 507 572 508 571 508 571 508 571 508 571 1618 572 508 571 1618 572 508 571 508 571 1619 571 508 651 20157 572 507 572 507 572 507 572 507 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 572 507 572 507 572 507 572 507 573 507 572 508 571 507 572 508 571 507 572 507 573 507 572 507 572 507 572 508 571 1619 571 507 572 1618 572 508 651 40424 9180 4422 599 1591 598 482 597 483 572 1618 572 507 598 482 597 482 572 507 572 507 598 482 572 507 598 482 572 507 598 482 597 482 572 507 572 507 572 507 573 507 572 507 573 507 572 1618 572 507 598 482 572 507 572 507 597 483 596 483 597 1593 572 1618 572 1618 572 508 572 507 572 1618 572 508 678 20157 572 507 597 482 597 482 597 481 598 482 572 507 572 507 572 507 572 507 597 482 572 507 597 482 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 508 572 507 572 507 572 507 572 507 572 507 572 508 571 508 571 1619 571 1618 572 508 571 507 572 -# -name: Cool_lo -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9075 4419 603 1586 603 476 602 478 600 1589 600 480 599 481 598 482 597 482 597 1593 597 1593 597 1593 597 482 597 482 572 507 572 507 596 483 573 507 572 507 573 507 598 482 572 507 573 1618 572 508 572 507 572 508 572 507 596 483 572 508 571 1618 596 484 572 1618 572 507 572 507 572 1618 572 508 651 20158 597 482 598 482 597 482 597 482 597 482 572 507 572 507 572 507 597 482 597 483 597 482 572 507 573 507 597 1594 572 507 597 482 572 507 597 482 598 482 572 508 571 508 571 508 572 507 572 507 597 482 597 483 572 508 571 508 571 508 596 483 597 1593 596 1593 652 40424 9181 4422 599 1591 598 481 598 482 572 1618 598 482 597 481 598 482 598 481 598 1593 597 1593 597 1592 598 482 598 482 597 482 597 482 598 482 597 482 597 482 598 482 597 482 597 482 598 1593 597 482 597 482 597 482 597 483 597 482 572 508 597 1593 596 1594 597 1593 597 483 571 508 571 1618 572 508 678 20157 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 573 507 572 507 573 507 572 507 573 507 572 507 572 507 572 507 572 508 571 507 572 508 571 508 571 508 571 508 572 1618 572 508 571 1618 572 -# -name: Heat_lo -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9048 4447 576 503 576 503 601 1589 600 1590 599 481 573 507 572 507 572 507 572 1618 572 1618 572 1618 572 507 572 507 573 507 572 508 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 572 507 573 507 572 508 571 507 572 507 572 508 571 1618 572 507 572 1618 572 508 571 507 572 1618 572 508 651 20157 572 507 572 507 572 507 572 507 572 508 572 507 572 507 572 507 572 508 571 508 572 508 571 508 571 508 571 1619 571 507 572 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 509 570 1620 570 1620 570 1620 570 1620 649 40424 9153 4450 573 506 573 507 572 1619 571 1618 572 508 571 508 571 508 572 508 571 1619 571 1619 571 1619 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 509 571 1619 571 509 571 509 570 509 571 509 570 509 570 509 570 1620 570 1621 569 1644 546 509 570 509 570 1620 570 510 676 20158 571 508 572 507 572 508 571 508 571 508 571 508 571 508 571 508 572 508 571 508 571 508 571 508 572 508 571 508 571 508 571 508 571 509 570 509 570 509 571 509 570 509 570 534 545 511 568 533 546 534 546 533 546 534 545 534 545 1645 545 534 545 1645 545 1644 546 -# -name: Heat_hi -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9072 4445 602 476 602 476 602 1588 600 1589 600 480 599 481 597 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 482 597 482 597 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 481 598 1592 598 481 598 481 598 1592 598 481 678 20126 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 597 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 482 597 481 598 481 598 1592 597 1592 597 481 677 40388 9180 4420 600 479 599 480 599 1591 599 1591 598 481 598 481 598 481 598 481 598 481 598 1591 598 1591 598 1591 599 480 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1591 598 481 598 481 598 481 598 481 598 481 598 481 598 1591 598 1592 598 1591 598 481 598 481 598 1592 598 481 704 20126 598 480 599 480 599 480 599 481 598 480 599 480 599 480 599 481 598 480 599 480 599 480 599 481 598 481 598 480 599 481 598 480 599 481 598 480 599 480 599 481 598 481 598 481 598 480 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1591 599 481 598 -# -name: Dh -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9076 4445 577 503 602 1587 603 477 601 1589 600 1590 599 481 573 507 572 507 572 1618 572 1618 572 1618 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 572 507 573 507 572 507 572 507 573 507 572 507 572 1618 572 507 573 1618 572 507 572 507 572 1618 572 507 652 20157 597 482 573 507 597 483 572 507 572 507 573 507 572 507 572 507 596 483 597 482 572 507 572 507 572 507 597 1593 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 596 484 572 507 572 507 572 507 597 1593 596 483 572 1618 572 1618 652 40424 9181 4422 599 480 598 1591 599 481 598 1592 598 1592 598 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 482 597 481 598 481 599 481 598 482 598 482 597 482 598 1592 598 482 597 482 597 482 597 482 597 482 598 482 597 1593 597 1593 597 1592 598 482 598 482 596 1593 597 482 703 20132 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 482 598 481 598 482 597 482 597 482 597 482 598 482 598 481 598 481 598 482 597 482 598 482 598 1592 598 482 597 482 597 482 598 482 597 482 597 482 572 507 572 507 598 482 597 1593 597 1593 572 -# # Model: LG Generic name: Off -type: parsed -protocol: NECext -address: 81 66 00 00 -command: 81 7E 00 00 -# +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8518 4239 547 1511 572 495 547 495 571 470 572 1512 571 470 570 471 569 473 569 1515 568 1515 568 474 568 473 568 475 567 473 568 473 568 474 567 475 567 473 567 474 568 473 568 474 567 1516 567 474 567 1516 567 475 567 474 567 474 567 1517 567 +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 8725 4071 527 1527 501 521 501 517 500 520 500 1545 499 523 498 521 521 525 495 529 470 550 470 549 494 527 470 552 471 549 470 552 471 547 495 529 470 548 471 1572 470 1574 470 548 494 1551 470 547 470 552 470 551 470 1571 496 1547 469 1573 471 -# -name: Heat_hi -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 8755 4064 528 1524 502 523 501 517 501 520 500 1543 500 519 498 524 497 523 497 527 496 521 496 525 521 500 496 527 496 1545 496 525 496 526 496 1545 495 1550 496 1541 496 1549 496 523 495 1546 496 526 496 523 521 499 496 1550 495 1544 495 1548 495 -# -name: Heat_lo -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 8783 4042 556 1497 502 521 501 518 501 520 500 1546 499 517 499 523 497 525 496 527 522 496 497 523 497 524 496 528 522 1516 521 500 496 526 497 1542 520 501 496 527 496 522 497 524 496 1548 496 521 521 500 521 500 520 503 497 521 521 500 497 -# +data: 8508 4216 570 1512 570 498 599 442 598 442 598 1485 597 445 595 446 594 448 566 476 566 475 566 476 566 475 566 475 566 476 565 474 566 475 566 476 566 474 566 475 566 1517 565 475 566 1518 566 475 567 1516 566 475 566 1516 567 1516 566 476 566 +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 8721 4070 527 1526 501 524 501 517 501 518 500 1544 500 519 498 524 497 526 496 550 471 549 470 549 470 551 494 531 470 548 470 549 470 552 470 1572 494 526 494 528 470 551 470 549 470 1573 470 551 470 548 495 1548 470 1572 469 551 494 527 495 -# +data: 8512 4239 546 1536 546 496 545 495 545 496 545 1539 569 471 568 473 542 499 542 500 542 499 542 501 541 499 542 499 542 500 542 499 542 500 541 1541 541 500 542 499 542 499 542 500 541 1542 542 500 541 1541 541 1541 542 1543 541 500 541 1542 540 +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 -data: 8780 4015 578 1498 553 471 526 494 525 494 524 1520 525 495 523 498 522 499 521 502 522 497 522 499 522 500 521 1519 522 500 521 499 521 1520 546 1496 521 503 523 494 522 500 521 499 522 1520 521 499 522 499 522 502 522 1518 521 501 521 1522 522 +data: 8518 4238 546 1535 547 494 546 495 546 495 546 1537 545 497 544 497 543 498 543 499 542 500 542 499 542 500 542 500 541 499 542 500 541 1542 541 501 541 499 541 500 541 1542 541 499 541 499 541 500 541 500 541 499 541 500 541 1542 541 499 541 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8512 4215 571 1512 570 497 599 442 598 443 597 1486 580 461 568 473 567 474 567 475 567 474 566 475 566 474 567 474 567 1516 566 475 567 474 566 1517 566 474 567 475 567 474 566 475 566 1516 566 474 567 1517 566 475 567 474 566 475 566 1518 566 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8514 4239 546 1536 546 495 546 494 546 495 546 1538 570 471 544 497 543 498 543 500 542 499 542 500 542 499 542 499 542 1541 541 501 541 499 541 1542 541 1543 540 1566 517 1567 517 524 517 1566 517 524 517 1567 516 1566 516 525 517 524 517 524 517 # # Model: Shivaki SSA18002 name: Off diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 11533746c..da679b4e7 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Checked 5th Aug, 2023 # name: Power type: parsed diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index bbae727a2..45cc810ab 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Updated 5th Aug, 2023 +# Last Checked 5th Aug, 2023 # name: Power type: raw @@ -1911,3 +1911,27 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1253 434 1258 435 492 1200 1258 438 1258 434 498 1194 494 1202 1257 433 494 1200 493 1200 493 1200 493 7940 1259 434 1258 433 493 1200 1259 438 1258 433 493 1199 494 1200 1259 434 492 1200 493 1200 493 1199 494 7939 1258 435 1257 434 497 1196 1258 437 1259 433 498 1196 492 1201 1258 434 497 1197 491 1200 493 1201 492 7941 1258 434 1258 435 495 1197 1258 437 1259 433 497 1197 497 1196 1259 434 496 1197 496 1197 496 1196 497 7939 1258 435 1257 434 495 1198 1258 438 1258 434 495 1198 495 1199 1257 435 494 1199 494 1197 496 1197 496 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 121 20917 311 387 1345 372 1316 372 450 1212 1319 400 1292 396 450 1210 479 1212 476 1238 450 1238 451 1238 451 1238 1345 7078 1316 372 1315 372 474 1216 1314 377 1314 374 472 1217 472 1217 472 1217 472 1217 472 1217 472 1217 1314 7106 1314 375 1313 374 472 1218 1313 378 1314 375 471 1218 471 1218 471 1218 471 1218 471 1218 471 1218 1313 7108 1312 376 1312 375 471 1219 1312 380 1312 376 470 1218 471 1218 471 1219 470 1218 471 1219 470 1219 1312 7110 1311 376 1312 376 470 1219 1312 381 1311 377 469 1219 470 1219 469 1220 469 1220 469 1220 469 1221 1310 7133 1286 402 1286 401 445 1245 1285 406 1286 401 445 1244 444 1244 445 1244 445 1244 445 1244 445 1245 1286 7134 1285 402 1286 402 444 1245 1286 406 1286 402 444 1245 444 1245 444 1245 444 1245 443 1245 444 1245 1285 7136 1284 403 1285 403 443 1245 1285 407 1285 403 443 1246 443 1246 443 1246 443 1246 443 1246 443 1246 1285 7135 1284 404 1284 404 442 1247 1284 408 1284 404 442 1247 442 1247 442 1247 442 1247 442 1248 441 1248 1283 7138 1282 405 1283 405 441 1249 1282 410 1281 407 440 1250 439 1249 440 1273 415 1274 390 1299 415 1274 1256 7166 1256 432 1256 431 415 1275 1255 436 1232 456 390 1299 415 1275 390 1299 390 1299 390 1299 390 1300 1231 7189 1231 457 1232 457 389 1301 1230 461 1231 458 388 1301 388 1301 388 1301 388 1326 363 1326 363 1327 1204 7217 1204 484 1205 484 362 1328 1203 488 1204 485 361 1328 361 1328 361 1328 361 1329 360 1353 335 1330 1202 7246 1177 511 1177 512 334 1356 1175 542 1150 539 307 1383 306 1383 306 1382 307 1409 280 1409 280 1410 1122 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1293 396 1293 395 450 1238 1294 399 1293 395 450 1238 451 1238 1294 395 450 1237 452 1237 452 1237 452 7964 1294 395 1318 370 475 1215 1316 377 1315 397 448 1241 448 1242 1290 398 447 1242 447 1242 447 1242 447 7970 1290 398 1292 372 473 1217 1316 377 1315 397 448 1217 473 1217 1316 372 473 1216 473 1217 473 1216 473 7944 1316 373 1316 372 473 1217 1316 376 1316 372 473 1216 473 1217 1316 372 473 1216 473 1216 473 1216 473 7942 1318 372 1317 371 474 1216 1317 375 1317 371 473 1216 473 1216 1318 371 474 1216 473 1216 473 1216 473 7943 1318 371 1318 371 474 1216 1318 375 1317 371 474 1216 474 1216 1318 371 474 1216 473 1216 473 1216 474 7943 1317 372 1317 372 473 1217 1317 376 1316 372 473 1217 472 1217 1316 372 473 1217 472 1217 472 1217 472 7944 1314 375 1314 398 447 1243 1290 403 1289 398 447 1243 446 1244 1289 399 446 1243 447 1243 446 1243 446 7971 1289 400 1289 399 446 1244 1289 403 1289 399 446 1243 446 1244 1289 399 446 1244 446 1244 445 1244 445 7972 1288 400 1289 399 446 1244 1290 403 1289 399 446 1244 445 1244 1289 399 446 1244 446 1244 445 1244 445 7946 1288 401 1288 400 469 1222 1311 380 1312 399 445 1244 445 1245 1289 399 445 1244 445 1245 445 1244 446 7971 1289 400 1289 400 444 1246 1289 403 1289 400 443 1246 444 1245 1272 416 445 1244 420 1269 444 1245 420 7996 1288 401 1288 400 420 1270 1264 429 1263 425 419 1270 419 1270 1264 425 419 1270 419 1270 419 1270 419 7995 1262 426 1263 425 419 1270 1263 430 1262 425 419 1270 419 1271 1262 426 418 1271 418 1271 418 1271 418 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1321 393 1295 393 451 1211 1322 396 1295 393 451 1210 507 1182 507 1181 1351 364 479 1208 480 1208 480 7933 1322 366 1320 368 475 1213 1319 373 1318 370 474 1214 474 1215 474 1215 1317 371 473 1215 473 1215 473 7939 1315 372 1316 372 472 1216 1316 376 1315 373 471 1217 472 1218 470 1218 1313 398 446 1242 446 1242 446 7967 1262 425 1263 425 444 1245 1263 429 1287 400 445 1244 445 1243 446 1244 1288 400 445 1243 446 1243 446 7966 1288 400 1288 400 445 1244 1287 404 1288 400 445 1244 444 1244 444 1245 1286 401 444 1244 444 1245 444 7968 1261 426 1286 401 419 1270 1285 406 1285 402 444 1244 445 1244 445 1245 1287 400 444 1244 445 1244 444 7968 1288 400 1288 400 444 1244 1288 404 1287 399 445 1244 444 1244 445 1244 1263 425 444 1244 444 1244 444 7967 1261 427 1261 426 418 1272 1260 456 1235 452 392 1296 392 1296 393 1297 1235 453 392 1296 393 1296 393 7996 1261 427 1261 427 418 1270 1262 430 1285 402 444 1245 443 1245 444 1245 1286 402 444 1245 444 1245 444 7969 1286 402 1286 402 444 1245 1286 406 1285 402 444 1245 444 1245 444 1245 1286 402 444 1245 444 1245 444 7968 1284 403 1285 403 443 1246 1284 407 1284 403 443 1246 442 1246 443 1246 1284 403 443 1246 443 1246 442 7971 1284 405 1283 405 441 1247 1283 408 1283 405 441 1248 441 1248 440 1249 1281 406 440 1249 440 1249 440 7974 1281 408 1280 431 390 1299 1232 460 1256 432 389 1299 390 1299 389 1300 1231 456 390 1299 390 1299 390 8023 1231 457 1231 457 389 1300 1231 461 1230 458 388 1300 389 1301 388 1301 1230 458 388 1302 387 1301 388 8027 1228 484 1204 484 362 1327 1204 488 1203 484 362 1328 361 1328 361 1329 1202 486 360 1354 335 1329 360 8080 1176 512 1176 512 334 1356 1175 542 1149 539 306 1382 307 1384 305 1383 1149 566 279 1436 252 1410 279 +# +name: Mode +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1373 341 1295 393 452 1211 1322 397 1295 393 451 1210 479 1210 479 1211 478 1237 1296 393 451 1236 453 7962 1321 367 1320 368 476 1214 1318 374 1318 370 474 1215 474 1215 474 1215 474 1216 1316 371 473 1215 474 7941 1316 372 1316 372 472 1217 1316 376 1316 372 473 1216 473 1216 473 1217 472 1217 1315 373 472 1217 472 7941 1315 374 1314 374 471 1218 1314 401 1291 398 447 1242 447 1242 447 1242 447 1243 1290 398 446 1242 447 7968 1290 399 1289 398 447 1243 1290 402 1290 398 447 1242 447 1242 447 1242 447 1243 1289 398 446 1243 446 7968 1289 399 1289 398 447 1243 1290 402 1290 398 446 1243 446 1243 446 1243 446 1243 1290 398 446 1243 446 7967 1289 399 1289 399 446 1244 1289 403 1289 398 446 1243 446 1243 446 1243 446 1244 1289 399 446 1243 446 7969 1288 400 1288 399 445 1244 1288 403 1288 400 445 1244 445 1244 445 1244 445 1244 1288 400 445 1244 420 7995 1261 427 1261 428 416 1296 1236 456 1236 452 392 1296 393 1296 393 1296 393 1296 1236 452 393 1296 393 7997 1260 428 1260 428 416 1273 1260 432 1259 428 417 1296 392 1272 417 1296 393 1296 1237 452 393 1296 393 8021 1236 452 1236 452 392 1297 1236 456 1236 452 393 1297 392 1297 392 1297 392 1298 1235 453 391 1298 391 diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 628a3f530..a2af75fe6 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Checked 5th Aug, 2023 # # ON name: Power diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 5c0a4c1a9..953fe6dbc 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Checked 5th Aug, 2023 # name: Power type: parsed diff --git a/assets/unit_tests/storage/md5.txt b/assets/unit_tests/storage/md5.txt new file mode 100644 index 000000000..777e390be --- /dev/null +++ b/assets/unit_tests/storage/md5.txt @@ -0,0 +1 @@ +Yo dawg, I heard you like md5... \ No newline at end of file diff --git a/fbt b/fbt index ef41cc056..471285a76 100755 --- a/fbt +++ b/fbt @@ -29,7 +29,7 @@ if [ -z "$FBT_NO_SYNC" ]; then echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi - git submodule update --init --depth 1 --jobs "$N_GIT_THREADS"; + git submodule update --init --jobs "$N_GIT_THREADS"; fi $SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 8dd50b537..17fbe82c4 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.3,, +Version,+,34.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -164,6 +164,7 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -627,6 +628,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b4edd8624..3f2ea7d46 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -212,6 +212,7 @@ Header,+,lib/subghz/subghz_worker.h,, Header,+,lib/subghz/transmitter.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -720,6 +721,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" diff --git a/furi/core/thread.c b/furi/core/thread.c index 62ab6fec4..4139f2ebc 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -60,10 +60,10 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); - furi_log_print_format( //-V576 + furi_log_print_format( thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, - "%s allocation balance: %u", + "%s allocation balance: %zu", thread->name ? thread->name : "Thread", thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index fc9dd06ba..539a48c85 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -892,7 +892,7 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); total_size += itref->value.size; } - FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); //-V576 + FURI_LOG_I(TAG, "Total size of loaded sections: %zu", total_size); } return status; diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index dd54cffb0..04386a675 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -4,6 +4,7 @@ #include #include #include "lfrfid_protocols.h" +#include #define FDX_B_ENCODED_BIT_SIZE (128) #define FDX_B_ENCODED_BYTE_SIZE (((FDX_B_ENCODED_BIT_SIZE) / 8)) @@ -323,8 +324,12 @@ void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, FuriString* result float temperature; if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { - float temperature_c = (temperature - 32) / 1.8; - furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + float temperature_c = (temperature - 32.0f) / 1.8f; + furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + } else { + furi_string_cat_printf(result, "T: %.2fF", (double)temperature); + } } else { furi_string_cat_printf(result, "T: ---"); } diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 3534745b7..5d7118092 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -244,7 +244,9 @@ bool subghz_file_encoder_worker_start( furi_stream_buffer_reset(instance->stream); furi_string_set(instance->file_path, file_path); - instance->device = subghz_devices_get_by_name(radio_device_name); + if(radio_device_name) { + instance->device = subghz_devices_get_by_name(radio_device_name); + } instance->worker_running = true; furi_thread_start(instance->thread); diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index fafedfeb4..cc531b5fa 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -10,6 +10,7 @@ env.Append( SDK_HEADERS=[ File("api_lock.h"), File("value_index.h"), + File("compress.h"), File("manchester_decoder.h"), File("manchester_encoder.h"), File("path.h"), diff --git a/lib/toolbox/md5_calc.c b/lib/toolbox/md5_calc.c new file mode 100644 index 000000000..b050295a1 --- /dev/null +++ b/lib/toolbox/md5_calc.c @@ -0,0 +1,44 @@ +#include "md5.h" +#include "md5_calc.h" + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error) { + bool result = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + const uint16_t size_to_read = 512; + uint8_t* data = malloc(size_to_read); + md5_context* md5_ctx = malloc(sizeof(md5_context)); + + md5_starts(md5_ctx); + while(true) { + uint16_t read_size = storage_file_read(file, data, size_to_read); + if(read_size == 0) break; + md5_update(md5_ctx, data, read_size); + } + md5_finish(md5_ctx, output); + free(md5_ctx); + free(data); + } + + if(file_error != NULL) { + *file_error = storage_file_get_error(file); + } + + storage_file_close(file); + return result; +} + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error) { + const size_t hash_size = 16; + unsigned char hash[hash_size]; + bool result = md5_calc_file(file, path, hash, file_error); + + if(result) { + furi_string_set(output, ""); + for(size_t i = 0; i < hash_size; i++) { + furi_string_cat_printf(output, "%02x", hash[i]); + } + } + + return result; +} \ No newline at end of file diff --git a/lib/toolbox/md5_calc.h b/lib/toolbox/md5_calc.h new file mode 100644 index 000000000..cf82e3718 --- /dev/null +++ b/lib/toolbox/md5_calc.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error); + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index ae850a8c3..57e60aecf 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -59,7 +59,9 @@ def extract_abs_dir_path(node): if abs_dir_node is None: raise StopError(f"Can't find absolute path for {node.name}") - return abs_dir_node.abspath + # Don't return abspath attribute (type is str), it will break in + # OverrideEnvironment.subst_list() by splitting path on spaces + return abs_dir_node def path_as_posix(path): diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 70b5762ca..dec4b8056 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -141,14 +141,14 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}', + '${PYTHON3} ${ASSETS_COMPILER} icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename "${ICON_FILE_NAME}"', "${ICONSCOMSTR}", ), emitter=icons_emitter, ), "ProtoBuilder": Builder( action=Action( - '${PYTHON3} "${NANOPB_COMPILER}" -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}', + "${PYTHON3} ${NANOPB_COMPILER} -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}", "${PROTOCOMSTR}", ), emitter=proto_emitter, @@ -157,14 +157,14 @@ def generate(env): ), "DolphinSymBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + "${PYTHON3} ${ASSETS_COMPILER} dolphin -s dolphin_${DOLPHIN_RES_TYPE} ${SOURCE} ${_DOLPHIN_OUT_DIR}", "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, ), "DolphinExtBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + "${PYTHON3} ${ASSETS_COMPILER} dolphin ${SOURCE} ${_DOLPHIN_OUT_DIR}", "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter,