diff --git a/build.ps1 b/build.ps1 index 0f82ee84e3b..31e45783c9a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -85,17 +85,17 @@ function Build-Run { } Write-Information 'Building with all the features enables' -Features-Configure -enable TOTP_BADBT_TYPE_ENABLED,TOTP_BADBT_TYPE_ICON_ENABLED +Features-Configure -enable TOTP_BADBT_TYPE_ENABLED,TOTP_AUTOMATION_ICONS_ENABLED Build-Run -FeaturesSuffix '' Write-Information 'Building with BadBT but without BadBT icon' -Features-Configure -disable TOTP_BADBT_TYPE_ICON_ENABLED +Features-Configure -disable TOTP_AUTOMATION_ICONS_ENABLED Build-Run -FeaturesSuffix '_badbt-wo-icon' Write-Information 'Building without BadBT' -Features-Configure -disable TOTP_BADBT_TYPE_ENABLED,TOTP_BADBT_TYPE_ICON_ENABLED +Features-Configure -disable TOTP_BADBT_TYPE_ENABLED,TOTP_AUTOMATION_ICONS_ENABLED Build-Run -FeaturesSuffix '_no-badbt' -Features-Configure -enable TOTP_BADBT_TYPE_ENABLED,TOTP_BADBT_TYPE_ICON_ENABLED +Features-Configure -enable TOTP_BADBT_TYPE_ENABLED,TOTP_AUTOMATION_ICONS_ENABLED Pop-Location \ No newline at end of file diff --git a/flipperzero-firmware_official_dev b/flipperzero-firmware_official_dev index 6089e9210f6..27341fc1934 160000 --- a/flipperzero-firmware_official_dev +++ b/flipperzero-firmware_official_dev @@ -1 +1 @@ -Subproject commit 6089e9210f67712284e14c147123938cd7be9bd9 +Subproject commit 27341fc1934ee309ee2ba1a2a42322449a12d7f4 diff --git a/totp/cli/cli.c b/totp/cli/cli.c index ce253080425..30876c7e42d 100644 --- a/totp/cli/cli.c +++ b/totp/cli/cli.c @@ -5,6 +5,7 @@ #include "cli_helpers.h" #include "commands/list/list.h" #include "commands/add/add.h" +#include "commands/update/update.h" #include "commands/delete/delete.h" #include "commands/timezone/timezone.h" #include "commands/help/help.h" @@ -13,6 +14,7 @@ #include "commands/notification/notification.h" #include "commands/reset/reset.h" #include "commands/automation/automation.h" +#include "commands/details/details.h" static void totp_cli_print_unknown_command(const FuriString* unknown_command) { TOTP_CLI_PRINTF_ERROR( @@ -62,6 +64,12 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { 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(cli, cli_context->event_queue); + } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) { + totp_cli_command_update_handle(plugin_state, args, cli); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DETAILS) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DETAILS_ALT) == 0) { + totp_cli_command_details_handle(plugin_state, args, cli); } else { totp_cli_print_unknown_command(cmd); } diff --git a/totp/cli/cli_helpers.c b/totp/cli/cli_helpers.c index 98463716463..8cafbdbfcfa 100644 --- a/totp/cli/cli_helpers.c +++ b/totp/cli/cli_helpers.c @@ -1,5 +1,6 @@ #include "cli_helpers.h" #include +#include #include "../types/plugin_event.h" bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { @@ -59,4 +60,22 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) { } return true; -} \ No newline at end of file +} + +bool args_read_uint8_and_trim(FuriString* args, uint8_t* value) { + int int_value; + if(!args_read_int_and_trim(args, &int_value) || int_value < 0 || int_value > UINT8_MAX) { + return false; + } + + *value = (uint8_t)int_value; + return true; +} + +void furi_string_secure_free(FuriString* str) { + for(long i = furi_string_size(str) - 1; i >= 0; i--) { + furi_string_set_char(str, i, '\0'); + } + + furi_string_free(str); +} diff --git a/totp/cli/cli_helpers.h b/totp/cli/cli_helpers.h index a35a2e59993..f3f8d963d46 100644 --- a/totp/cli/cli_helpers.h +++ b/totp/cli/cli_helpers.h @@ -89,3 +89,17 @@ void totp_cli_force_close_app(FuriMessageQueue* event_queue); * @return \c true if line successfully read and confirmed; \c false otherwise */ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input); + +/** + * @brief Extracts \c uint8_t value and trims arguments string + * @param args arguments string + * @param[out] value parsed value + * @return \c true if value successfully read and parsed as \c uint8_t ; \c false otherwise + */ +bool args_read_uint8_and_trim(FuriString* args, uint8_t* value); + +/** + * @brief Free \c FuriString instance in a secure manner by clearing it first + * @param str instance to free + */ +void furi_string_secure_free(FuriString* str); diff --git a/totp/cli/commands/add/add.c b/totp/cli/commands/add/add.c index 91f9256b297..eeb7aed8e88 100644 --- a/totp/cli/commands/add/add.c +++ b/totp/cli/commands/add/add.c @@ -7,44 +7,7 @@ #include "../../../services/convert/convert.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" - -#define TOTP_CLI_COMMAND_ADD_ARG_NAME "name" -#define TOTP_CLI_COMMAND_ADD_ARG_ALGO "algo" -#define TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "-a" -#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS "digits" -#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "-d" -#define TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX "-u" -#define TOTP_CLI_COMMAND_ADD_ARG_DURATION "duration" -#define TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX "-l" - -static bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { - if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) { - token_info->algo = SHA1; - return true; - } - - if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) { - token_info->algo = SHA256; - return true; - } - - if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) { - token_info->algo = SHA512; - return true; - } - - return false; -} - -static bool args_read_uint8_and_trim(FuriString* args, uint8_t* value) { - int int_value; - if(!args_read_int_and_trim(args, &int_value) || int_value < 0 || int_value > UINT8_MAX) { - return false; - } - - *value = (uint8_t)int_value; - return true; -} +#include "../../common_command_arguments.h" void totp_cli_command_add_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT @@ -54,42 +17,46 @@ void totp_cli_command_add_docopt_commands() { void totp_cli_command_add_docopt_usage() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NAME - " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_ALGO))) " " DOCOPT_OPTIONAL( + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL( DOCOPT_OPTION( - TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX, + TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_ADD_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX)) "\r\n"); + TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n"); } void totp_cli_command_add_docopt_arguments() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ARG_NAME " Token name\r\n"); } void totp_cli_command_add_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_OPTION( - TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX, - DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_ALGO)) " Token hashing algorithm.\r\n"); - TOTP_CLI_PRINTF( - " Could be one of: sha1, sha256, sha512 " DOCOPT_DEFAULT("sha1") "\r\n"); - cli_nl(); + TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_ARG_ALGO)) " Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME + ", " TOTP_TOKEN_ALGO_SHA256_NAME + ", " TOTP_TOKEN_ALGO_SHA512_NAME + " " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n"); TOTP_CLI_PRINTF(" " DOCOPT_OPTION( - TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX, + TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_ADD_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n"); + TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n"); TOTP_CLI_PRINTF(" " DOCOPT_OPTION( - TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX, + TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_ADD_ARG_DURATION)) " Token lifetime duration in seconds, between: 15 and 255 " DOCOPT_DEFAULT("30") "\r\n"); + TOTP_CLI_COMMAND_ARG_DURATION)) " Token lifetime duration in seconds, between: 15 and 255 " DOCOPT_DEFAULT("30") "\r\n"); TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( - TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) " Show console user input as-is without masking\r\n"); -} - -static void furi_string_secure_free(FuriString* str) { - for(long i = furi_string_size(str) - 1; i >= 0; i--) { - furi_string_set_char(str, i, '\0'); - } - - furi_string_free(str); + TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX) " Show console user input as-is without masking\r\n"); + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)) " Token automation features to be enabled. Must be one of: " TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME + ", " TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME + " " DOCOPT_DEFAULT( + TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) "\r\n"); + TOTP_CLI_PRINTF(" # " TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME + " - No features\r\n"); + TOTP_CLI_PRINTF(" # " TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME + " - Type key at the end of token input automation\r\n"); } void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { @@ -113,53 +80,12 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl bool mask_user_input = true; while(args_read_string_and_trim(args, temp_str)) { bool parsed = false; - if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX) == 0) { - if(!args_read_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINTF_ERROR( - "Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "\"\r\n"); - } else if(!token_info_set_algo_from_str(token_info, temp_str)) { - TOTP_CLI_PRINTF_ERROR( - "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX - "\"\r\n", - furi_string_get_cstr(temp_str)); - } else { - parsed = true; - } - } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX) == 0) { - uint8_t digit_value; - if(!args_read_uint8_and_trim(args, &digit_value)) { - TOTP_CLI_PRINTF_ERROR( - "Missed or incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX - "\"\r\n"); - } else if(!token_info_set_digits_from_int(token_info, digit_value)) { - TOTP_CLI_PRINTF_ERROR( - "\"%" PRIu8 - "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX - "\"\r\n", - digit_value); - } else { - parsed = true; - } - } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX) == 0) { - uint8_t duration_value; - if(!args_read_uint8_and_trim(args, &duration_value)) { - TOTP_CLI_PRINTF_ERROR( - "Missed or incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX - "\"\r\n"); - } else if(!token_info_set_duration_from_int(token_info, duration_value)) { - TOTP_CLI_PRINTF_ERROR( - "\"%" PRIu8 - "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX - "\"\r\n", - duration_value); - } else { - parsed = true; - } - } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) == 0) { - mask_user_input = false; - parsed = true; - } else { - TOTP_CLI_PRINTF_ERROR("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) { + totp_cli_printf_unknown_argument(temp_str); } if(!parsed) { diff --git a/totp/cli/commands/automation/automation.c b/totp/cli/commands/automation/automation.c index 1fd87f456b9..1af2e5dd72b 100644 --- a/totp/cli/commands/automation/automation.c +++ b/totp/cli/commands/automation/automation.c @@ -23,12 +23,12 @@ void totp_cli_command_automation_docopt_usage() { void totp_cli_command_automation_docopt_arguments() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD - " Automation method to be set. Must be one of [" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE + " 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 ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT #endif - "]\r\n"); + "\r\n"); } static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) { diff --git a/totp/cli/commands/delete/delete.c b/totp/cli/commands/delete/delete.c index 04cc815a44b..96a205f577d 100644 --- a/totp/cli/commands/delete/delete.c +++ b/totp/cli/commands/delete/delete.c @@ -7,9 +7,9 @@ #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" +#include "../../common_command_arguments.h" -#define TOTP_CLI_COMMAND_DELETE_ARG_INDEX "index" -#define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX "-f" +#define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX "-f" void totp_cli_command_delete_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE ", " TOTP_CLI_COMMAND_DELETE_ALT @@ -20,19 +20,23 @@ void totp_cli_command_delete_docopt_usage() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_DELETE " | " TOTP_CLI_COMMAND_DELETE_ALT) " " DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_DELETE_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX)) "\r\n"); + TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX)) "\r\n"); } void totp_cli_command_delete_docopt_arguments() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ARG_INDEX " Token index in the list\r\n"); } void totp_cli_command_delete_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( - TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX) " Force command to do not ask user for interactive confirmation\r\n"); + TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX) " Force command to do not ask user for interactive confirmation\r\n"); } void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || token_number > plugin_state->tokens_count) { @@ -43,10 +47,10 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, FuriString* temp_str = furi_string_alloc(); bool confirm_needed = true; if(args_read_string_and_trim(args, temp_str)) { - if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX) == 0) { + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX) == 0) { confirm_needed = false; } else { - TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + totp_cli_printf_unknown_argument(temp_str); TOTP_CLI_PRINT_INVALID_ARGUMENTS(); furi_string_free(temp_str); return; @@ -54,10 +58,6 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, } furi_string_free(temp_str); - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { - return; - } - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); TokenInfo* token_info = list_node->data; diff --git a/totp/cli/commands/details/details.c b/totp/cli/commands/details/details.c new file mode 100644 index 00000000000..75cd7030464 --- /dev/null +++ b/totp/cli/commands/details/details.c @@ -0,0 +1,61 @@ +#include "details.h" +#include +#include +#include "../../../lib/list/list.h" +#include "../../../types/token_info.h" +#include "../../../services/config/constants.h" +#include "../../cli_helpers.h" +#include "../../common_command_arguments.h" + +static void print_automation_features(const TokenInfo* token_info) { + if(token_info->automation_features == TOKEN_AUTOMATION_FEATURE_NONE) { + TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None"); + return; + } + + if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + TOTP_CLI_PRINTF( + "| %-20s | %-28.28s |\r\n", "Automation features", "Type key at the end"); + } +} + +void totp_cli_command_details_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DETAILS ", " TOTP_CLI_COMMAND_DETAILS_ALT + " Displays token details\r\n"); +} + +void totp_cli_command_details_docopt_usage() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED( + TOTP_CLI_COMMAND_DETAILS + " | " TOTP_CLI_COMMAND_DETAILS_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) "\r\n"); +} + +void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + int token_number; + if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || + token_number > plugin_state->tokens_count) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + return; + } + + ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + + TokenInfo* token_info = list_node->data; + + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); + TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Name", token_info->name); + TOTP_CLI_PRINTF( + "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); + TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); + TOTP_CLI_PRINTF( + "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); + print_automation_features(token_info); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); +} \ No newline at end of file diff --git a/totp/cli/commands/details/details.h b/totp/cli/commands/details/details.h new file mode 100644 index 00000000000..d141705a5f9 --- /dev/null +++ b/totp/cli/commands/details/details.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_DETAILS "lsattr" +#define TOTP_CLI_COMMAND_DETAILS_ALT "cat" + +void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_details_docopt_commands(); +void totp_cli_command_details_docopt_usage(); diff --git a/totp/cli/commands/help/help.c b/totp/cli/commands/help/help.c index 34b44debd2f..741098ad973 100644 --- a/totp/cli/commands/help/help.c +++ b/totp/cli/commands/help/help.c @@ -1,6 +1,7 @@ #include "help.h" #include "../../cli_helpers.h" #include "../add/add.h" +#include "../update/update.h" #include "../delete/delete.h" #include "../list/list.h" #include "../timezone/timezone.h" @@ -9,6 +10,7 @@ #include "../notification/notification.h" #include "../reset/reset.h" #include "../automation/automation.h" +#include "../details/details.h" void totp_cli_command_help_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT @@ -25,7 +27,9 @@ void totp_cli_command_help_handle() { TOTP_CLI_PRINTF("Usage:\r\n"); totp_cli_command_help_docopt_usage(); totp_cli_command_list_docopt_usage(); + totp_cli_command_details_docopt_usage(); totp_cli_command_add_docopt_usage(); + totp_cli_command_update_docopt_usage(); totp_cli_command_delete_docopt_usage(); totp_cli_command_timezone_docopt_usage(); totp_cli_command_move_docopt_usage(); @@ -37,7 +41,9 @@ void totp_cli_command_help_handle() { TOTP_CLI_PRINTF("Commands:\r\n"); totp_cli_command_help_docopt_commands(); totp_cli_command_list_docopt_commands(); + totp_cli_command_details_docopt_commands(); totp_cli_command_add_docopt_commands(); + totp_cli_command_update_docopt_commands(); totp_cli_command_delete_docopt_commands(); totp_cli_command_timezone_docopt_commands(); totp_cli_command_move_docopt_commands(); @@ -55,6 +61,7 @@ void totp_cli_command_help_handle() { cli_nl(); TOTP_CLI_PRINTF("Options:\r\n"); totp_cli_command_add_docopt_options(); + totp_cli_command_update_docopt_options(); totp_cli_command_delete_docopt_options(); totp_cli_command_move_docopt_options(); } \ No newline at end of file diff --git a/totp/cli/commands/list/list.c b/totp/cli/commands/list/list.c index 8ed9a136d22..6314b1b33b3 100644 --- a/totp/cli/commands/list/list.c +++ b/totp/cli/commands/list/list.c @@ -5,21 +5,6 @@ #include "../../../services/config/constants.h" #include "../../cli_helpers.h" -static char* get_algo_as_cstr(TokenHashAlgo algo) { - switch(algo) { - case SHA1: - return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME; - case SHA256: - return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME; - case SHA512: - return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME; - default: - break; - } - - return "UNKNOWN"; -} - void totp_cli_command_list_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_LIST ", " TOTP_CLI_COMMAND_LIST_ALT " List all available tokens\r\n"); @@ -41,8 +26,7 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { } TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); - TOTP_CLI_PRINTF( - "| %-*s | %-*s | %-*s | %-s | %-s |\r\n", 3, "#", 25, "Name", 6, "Algo", "Ln", "Dur"); + TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); uint16_t index = 1; TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { @@ -51,7 +35,7 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", index, token_info->name, - get_algo_as_cstr(token_info->algo), + token_info_get_algo_as_cstr(token_info), token_info->digits, token_info->duration); index++; diff --git a/totp/cli/commands/move/move.c b/totp/cli/commands/move/move.c index 1d5a23bbfeb..abb38143ed0 100644 --- a/totp/cli/commands/move/move.c +++ b/totp/cli/commands/move/move.c @@ -7,85 +7,50 @@ #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" - -#define TOTP_CLI_COMMAND_MOVE_ARG_INDEX "index" - -#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME "name" -#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX "-n" +#include "../../common_command_arguments.h" #define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "index" #define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX "-i" void totp_cli_command_move_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE ", " TOTP_CLI_COMMAND_MOVE_ALT - " Move\\rename token\r\n"); + " Move token\r\n"); } void totp_cli_command_move_docopt_usage() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NAME - " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_MOVE " | " TOTP_CLI_COMMAND_MOVE_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_INDEX) " " DOCOPT_OPTIONAL( + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_MOVE " | " TOTP_CLI_COMMAND_MOVE_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL( DOCOPT_OPTION( - TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX, - DOCOPT_ARGUMENT( - TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX))) "\r\n"); + TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX))) "\r\n"); } void totp_cli_command_move_docopt_options() { - TOTP_CLI_PRINTF(" " DOCOPT_OPTION( - TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX, - DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME)) " New token name\r\n"); TOTP_CLI_PRINTF(" " DOCOPT_OPTION( TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX)) " New token index\r\n"); } void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - int token_index; - if(!args_read_int_and_trim(args, &token_index)) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - return; - } - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } - if(token_index < 1 || token_index > plugin_state->tokens_count) { + int token_index; + if(!args_read_int_and_trim(args, &token_index) || token_index < 1 || + token_index > plugin_state->tokens_count) { TOTP_CLI_PRINT_INVALID_ARGUMENTS(); return; } FuriString* temp_str = furi_string_alloc(); - char* new_token_name = NULL; int new_token_index = 0; while(args_read_string_and_trim(args, temp_str)) { bool parsed = false; - if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX) == 0) { - if(!args_read_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINTF_ERROR( - "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX - "\"\r\n"); - } else { - if(new_token_name != NULL) { - free(new_token_name); - } - - new_token_name = malloc(furi_string_size(temp_str) + 1); - if(new_token_name == NULL) { - furi_string_free(temp_str); - return; - } - - strlcpy( - new_token_name, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str) + 1); - parsed = true; - } - } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX) == 0) { + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX) == 0) { if(!args_read_int_and_trim(args, &new_token_index)) { TOTP_CLI_PRINTF_ERROR( "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX @@ -106,18 +71,12 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C if(!parsed) { TOTP_CLI_PRINT_INVALID_ARGUMENTS(); furi_string_free(temp_str); - if(new_token_name != NULL) { - free(new_token_name); - } return; } } if(!totp_cli_ensure_authenticated(plugin_state, cli)) { furi_string_free(temp_str); - if(new_token_name != NULL) { - free(new_token_name); - } return; } @@ -140,12 +99,6 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C token_info = list_element_at(plugin_state->tokens_list, token_index - 1)->data; } - if(new_token_name != NULL) { - free(token_info->name); - token_info->name = new_token_name; - token_updated = true; - } - if(token_updated) { if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { TOTP_CLI_PRINTF_SUCCESS( diff --git a/totp/cli/commands/notification/notification.c b/totp/cli/commands/notification/notification.c index 016b83d0d66..b81b7371a5b 100644 --- a/totp/cli/commands/notification/notification.c +++ b/totp/cli/commands/notification/notification.c @@ -23,9 +23,9 @@ void totp_cli_command_notification_docopt_usage() { void totp_cli_command_notification_docopt_arguments() { TOTP_CLI_PRINTF( " " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD - " Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE + " Notification method to be set. Must be one of: " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND - ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n"); + ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n"); } static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) { diff --git a/totp/cli/commands/update/update.c b/totp/cli/commands/update/update.c new file mode 100644 index 00000000000..669a36db897 --- /dev/null +++ b/totp/cli/commands/update/update.c @@ -0,0 +1,162 @@ +#include "update.h" +#include +#include +#include "../../../lib/list/list.h" +#include "../../../types/token_info.h" +#include "../../../services/config/config.h" +#include "../../../services/convert/convert.h" +#include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" +#include "../../common_command_arguments.h" + +#define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s" + +void totp_cli_command_update_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); +} + +void totp_cli_command_update_docopt_usage() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL( + DOCOPT_OPTION( + TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n"); +} + +void totp_cli_command_update_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_ARG_NAME_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) " Token name\r\n"); +} + +static bool + totp_cli_try_read_name(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { + if(!args_read_probably_quoted_string_and_trim(args, arg) || furi_string_empty(arg)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); + } else { + if(token_info->name != NULL) { + free(token_info->name); + } + + size_t temp_cstr_len = furi_string_size(arg); + token_info->name = malloc(temp_cstr_len + 1); + furi_check(token_info->name != NULL); + strlcpy(token_info->name, furi_string_get_cstr(arg), temp_cstr_len + 1); + *parsed = true; + } + + return true; + } + + return false; +} + +static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { + *flag = true; + *parsed = true; + return true; + } + + return false; +} + +void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + FuriString* temp_str = furi_string_alloc(); + + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + int token_number; + if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || + token_number > plugin_state->tokens_count) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + return; + } + + ListNode* list_item = list_element_at(plugin_state->tokens_list, token_number - 1); + TokenInfo* existing_token_info = list_item->data; + TokenInfo* token_info = token_info_clone(existing_token_info); + + // Read optional arguments + bool mask_user_input = true; + bool update_token_secret = false; + while(args_read_string_and_trim(args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && + !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + furi_string_free(temp_str); + token_info_free(token_info); + return; + } + } + + if(update_token_secret) { + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + if(!totp_cli_read_line(cli, temp_str, mask_user_input) || + !totp_cli_ensure_authenticated(plugin_state, cli)) { + TOTP_CLI_DELETE_LAST_LINE(); + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); + furi_string_secure_free(temp_str); + token_info_free(token_info); + return; + } + + TOTP_CLI_DELETE_LAST_LINE(); + + if(token_info->token != NULL) { + free(token_info->token); + } + + if(!token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + plugin_state->iv)) { + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + furi_string_secure_free(temp_str); + token_info_free(token_info); + return; + } + } + + furi_string_secure_free(temp_str); + + bool load_generate_token_scene = false; + if(plugin_state->current_scene == TotpSceneGenerateToken) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + load_generate_token_scene = true; + } + + list_item->data = token_info; + + if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully updated\r\n", token_info->name); + token_info_free(existing_token_info); + } else { + TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + list_item->data = existing_token_info; + token_info_free(token_info); + } + + if(load_generate_token_scene) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } +} \ No newline at end of file diff --git a/totp/cli/commands/update/update.h b/totp/cli/commands/update/update.h new file mode 100644 index 00000000000..3b5b442e88e --- /dev/null +++ b/totp/cli/commands/update/update.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_UPDATE "update" + +void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_update_docopt_commands(); +void totp_cli_command_update_docopt_usage(); +void totp_cli_command_update_docopt_options(); \ No newline at end of file diff --git a/totp/cli/common_command_arguments.c b/totp/cli/common_command_arguments.c new file mode 100644 index 00000000000..0ef121add9a --- /dev/null +++ b/totp/cli/common_command_arguments.c @@ -0,0 +1,112 @@ +#include "common_command_arguments.h" +#include + +inline void totp_cli_printf_missed_argument_value(char* arg) { + TOTP_CLI_PRINTF_ERROR("Missed or incorrect value for argument \"%s\"\r\n", arg); +} + +inline void totp_cli_printf_unknown_argument(const FuriString* arg) { + TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(arg)); +} + +bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_ALGO_PREFIX) == 0) { + if(!args_read_string_and_trim(args, arg)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX); + } else if(!token_info_set_algo_from_str(token_info, arg)) { + TOTP_CLI_PRINTF_ERROR( + "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_ALGO_PREFIX + "\"\r\n", + furi_string_get_cstr(arg)); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +bool totp_cli_try_read_digits( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX) == 0) { + uint8_t digit_value; + if(!args_read_uint8_and_trim(args, &digit_value)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX); + } else if(!token_info_set_digits_from_int(token_info, digit_value)) { + TOTP_CLI_PRINTF_ERROR( + "\"%" PRIu8 + "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX + "\"\r\n", + digit_value); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +bool totp_cli_try_read_duration( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_DURATION_PREFIX) == 0) { + uint8_t duration_value; + if(!args_read_uint8_and_trim(args, &duration_value)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX); + } else if(!token_info_set_duration_from_int(token_info, duration_value)) { + TOTP_CLI_PRINTF_ERROR( + "\"%" PRIu8 + "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_DURATION_PREFIX + "\"\r\n", + duration_value); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +bool totp_cli_try_read_automation_features( + TokenInfo* token_info, + FuriString* arg, + FuriString* args, + bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX) == 0) { + if(!args_read_string_and_trim(args, arg)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX); + } else if(!token_info_set_automation_feature_from_str(token_info, arg)) { + TOTP_CLI_PRINTF_ERROR( + "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX + "\"\r\n", + furi_string_get_cstr(arg)); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX) == 0) { + *unsecure_flag = false; + *parsed = true; + return true; + } + + return false; +} \ No newline at end of file diff --git a/totp/cli/common_command_arguments.h b/totp/cli/common_command_arguments.h new file mode 100644 index 00000000000..85413a321dd --- /dev/null +++ b/totp/cli/common_command_arguments.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include "../types/token_info.h" +#include "cli_helpers.h" + +#define TOTP_CLI_COMMAND_ARG_NAME "name" +#define TOTP_CLI_COMMAND_ARG_NAME_PREFIX "-n" +#define TOTP_CLI_COMMAND_ARG_ALGO "algo" +#define TOTP_CLI_COMMAND_ARG_ALGO_PREFIX "-a" +#define TOTP_CLI_COMMAND_ARG_DIGITS "digits" +#define TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX "-d" +#define TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX "-u" +#define TOTP_CLI_COMMAND_ARG_DURATION "duration" +#define TOTP_CLI_COMMAND_ARG_DURATION_PREFIX "-l" +#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b" +#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature" +#define TOTP_CLI_COMMAND_ARG_INDEX "index" + +void totp_cli_printf_unknown_argument(const FuriString* arg); +void totp_cli_printf_missed_argument_value(char* arg); +bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); +bool totp_cli_try_read_digits( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed); +bool totp_cli_try_read_duration( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed); +bool totp_cli_try_read_automation_features( + TokenInfo* token_info, + FuriString* arg, + FuriString* args, + bool* parsed); +bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); \ No newline at end of file diff --git a/totp/features_config.h b/totp/features_config.h index d3b30aee017..d848b0acdfc 100644 --- a/totp/features_config.h +++ b/totp/features_config.h @@ -1,2 +1,2 @@ #define TOTP_BADBT_TYPE_ENABLED -#define TOTP_BADBT_TYPE_ICON_ENABLED \ No newline at end of file +#define TOTP_AUTOMATION_ICONS_ENABLED \ No newline at end of file diff --git a/totp/images/hid_ble_10x7.png b/totp/images/hid_ble_10x7.png deleted file mode 100644 index 3cd1ff95c83..00000000000 Binary files a/totp/images/hid_ble_10x7.png and /dev/null differ diff --git a/totp/images/hid_ble_31x9.png b/totp/images/hid_ble_31x9.png new file mode 100644 index 00000000000..fb999231f11 Binary files /dev/null and b/totp/images/hid_ble_31x9.png differ diff --git a/totp/images/hid_usb_31x9.png b/totp/images/hid_usb_31x9.png new file mode 100644 index 00000000000..c6b8aa22823 Binary files /dev/null and b/totp/images/hid_usb_31x9.png differ diff --git a/totp/services/config/config.c b/totp/services/config/config.c index 66c07ebfa82..52065ad32d0 100644 --- a/totp/services/config/config.c +++ b/totp/services/config/config.c @@ -5,8 +5,7 @@ #include "../../types/common.h" #include "../../types/token_info.h" #include "../../features_config.h" -#include "migrations/config_migration_v1_to_v2.h" -#include "migrations/config_migration_v2_to_v3.h" +#include "migrations/common_migration.h" #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" @@ -15,31 +14,6 @@ #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig" #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf" -static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { - switch(token_info->algo) { - case SHA1: - return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME; - case SHA256: - return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME; - case SHA512: - return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME; - default: - break; - } - - return NULL; -} - -static void token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { - if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) { - token_info->algo = SHA1; - } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) { - token_info->algo = SHA256; - } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) { - token_info->algo = SHA512; - } -} - /** * @brief Opens storage record * @return Storage record @@ -166,13 +140,13 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF furi_string_printf( temp_str, " # Token hashing algorithm to use during code generation. Supported options are %s, %s and %s. If you are not use which one to use - use %s", - TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME, - TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME, - TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME, - TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME); + TOTP_TOKEN_ALGO_SHA1_NAME, + TOTP_TOKEN_ALGO_SHA256_NAME, + TOTP_TOKEN_ALGO_SHA512_NAME, + TOTP_TOKEN_ALGO_SHA1_NAME); flipper_format_write_comment(fff_data_file, temp_str); furi_string_printf( - temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME); + temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); flipper_format_write_comment(fff_data_file, temp_str); flipper_format_write_comment_cstr(fff_data_file, " "); @@ -190,6 +164,13 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF flipper_format_write_comment(fff_data_file, temp_str); flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr( + fff_data_file, + "# Token input automation features (0 - None, 1 - press \"Enter\" key at the end of automation)"); + furi_string_printf(temp_str, "%s: 0", TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES); + flipper_format_write_comment(fff_data_file, temp_str); + flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ==="); flipper_format_write_comment_cstr(fff_data_file, " "); @@ -255,6 +236,13 @@ static TotpConfigFileUpdateResult break; } + tmp_uint32 = token_info->automation_features; + if(!flipper_format_write_uint32( + file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { + update_result = TotpConfigFileUpdateError; + break; + } + update_result = TotpConfigFileUpdateSuccess; } while(false); @@ -544,28 +532,19 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st break; } - if(file_version == 1) { - if(totp_config_migrate_v1_to_v2(fff_data_file, fff_backup_data_file)) { - FURI_LOG_I(LOGGING_TAG, "Applied migration from v1 to v2"); - file_version = 2; - } else { - FURI_LOG_W( - LOGGING_TAG, "An error occurred during migration from v1 to v2"); - result = TotpConfigFileOpenError; - break; - } - } - - if(file_version == 2) { - if(totp_config_migrate_v2_to_v3(fff_data_file, fff_backup_data_file)) { - FURI_LOG_I(LOGGING_TAG, "Applied migration from v2 to v3"); - file_version = 3; - } else { - FURI_LOG_W( - LOGGING_TAG, "An error occurred during migration from v2 to v3"); - result = TotpConfigFileOpenError; - break; - } + if(totp_config_migrate_to_latest(fff_data_file, fff_backup_data_file)) { + FURI_LOG_I( + LOGGING_TAG, + "Applied migration to version %" PRId16, + CONFIG_FILE_ACTUAL_VERSION); + file_version = CONFIG_FILE_ACTUAL_VERSION; + } else { + FURI_LOG_W( + LOGGING_TAG, + "An error occurred during migration to version %" PRId16, + CONFIG_FILE_ACTUAL_VERSION); + result = TotpConfigFileOpenError; + break; } flipper_format_file_close(fff_backup_data_file); @@ -743,9 +722,8 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) } } - if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) { - token_info_set_algo_from_str(tokenInfo, temp_str); - } else { + if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str) || + !token_info_set_algo_from_str(tokenInfo, temp_str)) { tokenInfo->algo = SHA1; } @@ -761,6 +739,13 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; } + if(flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { + tokenInfo->automation_features = temp_data32; + } else { + tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + } + FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name); TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); diff --git a/totp/services/config/constants.h b/totp/services/config/constants.h index 526179f4128..9924aefe2a1 100644 --- a/totp/services/config/constants.h +++ b/totp/services/config/constants.h @@ -1,7 +1,7 @@ #pragma once #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION 3 +#define CONFIG_FILE_ACTUAL_VERSION 4 #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" @@ -9,12 +9,9 @@ #define TOTP_CONFIG_KEY_TOKEN_ALGO "TokenAlgo" #define TOTP_CONFIG_KEY_TOKEN_DIGITS "TokenDigits" #define TOTP_CONFIG_KEY_TOKEN_DURATION "TokenDuration" +#define TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES "TokenAutomationFeatures" #define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto" #define TOTP_CONFIG_KEY_BASE_IV "BaseIV" #define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" #define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod" - -#define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1" -#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256" -#define TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME "sha512" diff --git a/totp/services/config/migrations/common_migration.c b/totp/services/config/migrations/common_migration.c new file mode 100644 index 00000000000..073eaab12da --- /dev/null +++ b/totp/services/config/migrations/common_migration.c @@ -0,0 +1,129 @@ +#include "common_migration.h" +#include "../constants.h" +#include "../../../types/token_info.h" + +bool totp_config_migrate_to_latest( + FlipperFormat* fff_data_file, + FlipperFormat* fff_backup_data_file) { + FuriString* temp_str = furi_string_alloc(); + uint32_t current_version = 0; + bool result = false; + do { + flipper_format_write_header_cstr( + fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION); + + if(!flipper_format_read_header(fff_backup_data_file, temp_str, ¤t_version)) { + break; + } + + 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); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_PINSET, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_PINSET, temp_str); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, temp_str)) { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, temp_str); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, temp_str)) { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, temp_str); + } + + flipper_format_rewind(fff_backup_data_file); + + FuriString* comment_str = furi_string_alloc(); + + while(true) { + if(!flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { + break; + } + + furi_string_printf( + comment_str, "=== BEGIN \"%s\" ===", furi_string_get_cstr(temp_str)); + flipper_format_write_comment(fff_data_file, comment_str); + furi_string_printf(comment_str, "=== END \"%s\" ===", furi_string_get_cstr(temp_str)); + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str); + + flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str); + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str); + + if(current_version > 1) { + flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + + flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); + } else { + flipper_format_write_string_cstr( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); + const uint32_t default_digits = TOTP_6_DIGITS; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1); + } + + if(current_version > 2) { + flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, temp_str); + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, temp_str); + } else { + const uint32_t default_duration = TOTP_TOKEN_DURATION_DEFAULT; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &default_duration, 1); + } + + if(current_version > 3) { + flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str); + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str); + } else { + const uint32_t default_automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + flipper_format_write_uint32( + fff_data_file, + TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, + &default_automation_features, + 1); + } + + flipper_format_write_comment(fff_data_file, comment_str); + } + + furi_string_free(comment_str); + + result = true; + } while(false); + + furi_string_free(temp_str); + return result; +} \ No newline at end of file diff --git a/totp/services/config/migrations/config_migration_v2_to_v3.h b/totp/services/config/migrations/common_migration.h similarity index 54% rename from totp/services/config/migrations/config_migration_v2_to_v3.h rename to totp/services/config/migrations/common_migration.h index e3078db14c8..71defc38455 100644 --- a/totp/services/config/migrations/config_migration_v2_to_v3.h +++ b/totp/services/config/migrations/common_migration.h @@ -2,6 +2,6 @@ #include -bool totp_config_migrate_v2_to_v3( +bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file); + FlipperFormat* fff_backup_data_file); \ No newline at end of file diff --git a/totp/services/config/migrations/config_migration_v1_to_v2.c b/totp/services/config/migrations/config_migration_v1_to_v2.c deleted file mode 100644 index c2ca8f90536..00000000000 --- a/totp/services/config/migrations/config_migration_v1_to_v2.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "config_migration_v1_to_v2.h" -#include -#include "../constants.h" -#include "../../../types/token_info.h" - -#define NEW_VERSION 2 - -bool totp_config_migrate_v1_to_v2( - FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file) { - flipper_format_write_header_cstr(fff_data_file, CONFIG_FILE_HEADER, NEW_VERSION); - - FuriString* temp_str = furi_string_alloc(); - - 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); - } - - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str); - } - - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str); - } - - while(true) { - if(!flipper_format_read_string( - fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { - break; - } - - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str); - - flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str); - - flipper_format_write_string_cstr( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME); - const uint32_t default_digits = TOTP_6_DIGITS; - flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1); - } - - furi_string_free(temp_str); - return true; -} diff --git a/totp/services/config/migrations/config_migration_v1_to_v2.h b/totp/services/config/migrations/config_migration_v1_to_v2.h deleted file mode 100644 index 99470f04d64..00000000000 --- a/totp/services/config/migrations/config_migration_v1_to_v2.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -bool totp_config_migrate_v1_to_v2( - FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file); diff --git a/totp/services/config/migrations/config_migration_v2_to_v3.c b/totp/services/config/migrations/config_migration_v2_to_v3.c deleted file mode 100644 index 995b3b02e2a..00000000000 --- a/totp/services/config/migrations/config_migration_v2_to_v3.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "config_migration_v2_to_v3.h" -#include -#include "../constants.h" -#include "../../../types/token_info.h" - -#define NEW_VERSION 3 - -bool totp_config_migrate_v2_to_v3( - FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file) { - flipper_format_write_header_cstr(fff_data_file, CONFIG_FILE_HEADER, NEW_VERSION); - - FuriString* temp_str = furi_string_alloc(); - - 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); - } - - flipper_format_rewind(fff_backup_data_file); - - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str); - } - - flipper_format_rewind(fff_backup_data_file); - - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str); - } - - flipper_format_rewind(fff_backup_data_file); - - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_PINSET, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_PINSET, temp_str); - } - - flipper_format_rewind(fff_backup_data_file); - - if(flipper_format_read_string( - fff_backup_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, temp_str); - } - - 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)) { - break; - } - - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str); - - flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str); - - flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); - - flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); - - const uint32_t default_duration = TOTP_TOKEN_DURATION_DEFAULT; - flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &default_duration, 1); - } - - furi_string_free(temp_str); - return true; -} diff --git a/totp/types/token_info.c b/totp/types/token_info.c index b69979cc71f..49c0d098c44 100644 --- a/totp/types/token_info.c +++ b/totp/types/token_info.c @@ -1,4 +1,3 @@ -#include #include #include "token_info.h" #include "stdlib.h" @@ -6,6 +5,7 @@ #include "../lib/base32/base32.h" #include "../services/crypto/crypto.h" #include "../lib/polyfills/memset_s.h" +#include "../lib/polyfills/strnlen.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); @@ -13,6 +13,7 @@ TokenInfo* token_info_alloc() { tokenInfo->algo = SHA1; tokenInfo->digits = TOTP_6_DIGITS; tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; + tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; return tokenInfo; } @@ -70,4 +71,68 @@ bool token_info_set_duration_from_int(TokenInfo* token_info, uint8_t duration) { } return false; +} + +bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { + if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_SHA1_NAME) == 0) { + token_info->algo = SHA1; + return true; + } + + if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_SHA256_NAME) == 0) { + token_info->algo = SHA256; + return true; + } + + if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_SHA512_NAME) == 0) { + token_info->algo = SHA512; + return true; + } + + return false; +} + +char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { + switch(token_info->algo) { + case SHA1: + return TOTP_TOKEN_ALGO_SHA1_NAME; + case SHA256: + return TOTP_TOKEN_ALGO_SHA256_NAME; + case SHA512: + return TOTP_TOKEN_ALGO_SHA512_NAME; + default: + break; + } + + return NULL; +} + +bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const FuriString* str) { + if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME) == 0) { + token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END; + return true; + } + + if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) == 0) { + token_info->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + return true; + } + + return false; +} + +TokenInfo* token_info_clone(const TokenInfo* src) { + TokenInfo* clone = token_info_alloc(); + memcpy(clone, src, sizeof(TokenInfo)); + + clone->token = malloc(src->token_length); + furi_check(clone->token != NULL); + memcpy(clone->token, src->token, src->token_length); + + int name_length = strnlen(src->name, TOTP_TOKEN_MAX_LENGTH); + clone->name = malloc(name_length + 1); + furi_check(clone->name != NULL); + strlcpy(clone->name, src->name, name_length + 1); + + return clone; } \ No newline at end of file diff --git a/totp/types/token_info.h b/totp/types/token_info.h index d04564c4219..c4f168eb738 100644 --- a/totp/types/token_info.h +++ b/totp/types/token_info.h @@ -1,11 +1,22 @@ #pragma once #include +#include +#include #define TOTP_TOKEN_DURATION_DEFAULT 30 +#define TOTP_TOKEN_ALGO_SHA1_NAME "sha1" +#define TOTP_TOKEN_ALGO_SHA256_NAME "sha256" +#define TOTP_TOKEN_ALGO_SHA512_NAME "sha512" +#define TOTP_TOKEN_MAX_LENGTH 255 + +#define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none" +#define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter" + typedef uint8_t TokenHashAlgo; typedef uint8_t TokenDigitsCount; +typedef uint8_t TokenAutomationFeature; /** * @brief Hashing algorithm to be used to generate token @@ -42,6 +53,21 @@ enum TokenDigitsCounts { TOTP_8_DIGITS = 8 }; +/** + * @brief Token automation features. + */ +enum TokenAutomationFeatures { + /** + * @brief No features enabled + */ + TOKEN_AUTOMATION_FEATURE_NONE = 0b00, + + /** + * @brief Press "Enter" key at the end as a part of token input automation + */ + TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END = 0b01 +}; + #define TOTP_TOKEN_DIGITS_MAX_COUNT 8 /** @@ -77,6 +103,11 @@ typedef struct { * @brief Desired TOTP token duration in seconds */ uint8_t duration; + + /** + * @brief Token input automation features + */ + TokenAutomationFeature automation_features; } TokenInfo; /** @@ -120,3 +151,33 @@ bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits); * @return \c true if token duration has been updated; \c false otherwise */ bool token_info_set_duration_from_int(TokenInfo* token_info, uint8_t duration); + +/** + * @brief Sets token hashing algorithm from \c str value + * @param token_info instance whichs token hashing algorithm should be updated + * @param str desired token algorithm + * @return \c true if token hahsing algorithm has been updated; \c false otherwise + */ +bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str); + +/** + * @brief Gets token hahsing algorithm name as C-string + * @param token_info instance which token hahsing algorithm name should be returned + * @return token hashing algorithm name as C-string + */ +char* token_info_get_algo_as_cstr(const TokenInfo* token_info); + +/** + * @brief Sets token automation feature from \c str value + * @param token_info instance whichs token automation feature should be updated + * @param str desired token automation feature + * @return \c true if token automation feature has been set; \c false otherwise + */ +bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const FuriString* str); + +/** + * @brief Clones \c TokenInfo instance + * @param src instance to clone + * @return cloned instance + */ +TokenInfo* token_info_clone(const TokenInfo* src); \ No newline at end of file diff --git a/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/totp/ui/scenes/generate_token/totp_scene_generate_token.c index 9e8b21d09ac..d3b27fdc707 100644 --- a/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -350,14 +350,34 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } -#if defined(TOTP_BADBT_TYPE_ENABLED) && defined(TOTP_BADBT_TYPE_ICON_ENABLED) +#ifdef TOTP_AUTOMATION_ICONS_ENABLED + if(plugin_state->automation_method & AutomationMethodBadUsb) { + canvas_draw_icon( + canvas, +#ifdef TOTP_BADBT_TYPE_ENABLED + SCREEN_WIDTH_CENTER - + (plugin_state->automation_method & AutomationMethodBadBt ? 33 : 15), +#else + SCREEN_WIDTH_CENTER - 15, +#endif + + SCREEN_HEIGHT_CENTER + 12, + &I_hid_usb_31x9); + } + +#ifdef TOTP_BADBT_TYPE_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt && plugin_state->bt_type_code_worker_context != NULL && plugin_state->bt_type_code_worker_context->is_advertising) { canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, SCREEN_HEIGHT_CENTER + 13, &I_hid_ble_10x7); + canvas, + SCREEN_WIDTH_CENTER + + (plugin_state->automation_method & AutomationMethodBadUsb ? 2 : -15), + SCREEN_HEIGHT_CENTER + 12, + &I_hid_ble_31x9); } #endif +#endif } bool totp_scene_generate_token_handle_event( @@ -373,20 +393,27 @@ bool totp_scene_generate_token_handle_event( SceneState* scene_state; if(event->input.type == InputTypeLong) { - if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) { + if(event->input.key == InputKeyDown && + plugin_state->automation_method & AutomationMethodBadUsb) { scene_state = (SceneState*)plugin_state->current_scene_state; totp_usb_type_code_worker_notify( - scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType); + scene_state->usb_type_code_worker_context, + TotpUsbTypeCodeWorkerEventType, + scene_state->current_token->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); return true; } #ifdef TOTP_BADBT_TYPE_ENABLED - else if(event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { + else if( + event->input.key == InputKeyUp && + plugin_state->automation_method & AutomationMethodBadBt) { scene_state = (SceneState*)plugin_state->current_scene_state; totp_bt_type_code_worker_notify( - plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType); + plugin_state->bt_type_code_worker_context, + TotpBtTypeCodeWorkerEventType, + scene_state->current_token->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); diff --git a/totp/workers/bt_type_code/bt_type_code.c b/totp/workers/bt_type_code/bt_type_code.c index 8b9cf6548b5..7962a152d78 100644 --- a/totp/workers/bt_type_code/bt_type_code.c +++ b/totp/workers/bt_type_code/bt_type_code.c @@ -2,6 +2,7 @@ #include #include #include "../../types/common.h" +#include "../../types/token_info.h" #include "../../services/convert/convert.h" #include "../constants.h" @@ -11,26 +12,36 @@ static inline bool totp_type_code_worker_stop_requested() { return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; } +static void totp_type_code_worker_press_key(uint8_t key) { + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(30); + furi_hal_bt_hid_kb_release(key); +} + static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) { + TokenAutomationFeature features = context->flags; uint8_t i = 0; do { furi_delay_ms(500); i++; - } while(!furi_hal_bt_is_active() && i < 100 && !totp_type_code_worker_stop_requested()); + } while(!context->is_connected && i < 100 && !totp_type_code_worker_stop_requested()); - if(furi_hal_bt_is_active() && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + if(context->is_connected && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { furi_delay_ms(500); i = 0; while(i < context->string_length && context->string[i] != 0) { uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]); if(digit > 9) break; uint8_t hid_kb_key = hid_number_keys[digit]; - furi_hal_bt_hid_kb_press(hid_kb_key); - furi_delay_ms(30); - furi_hal_bt_hid_kb_release(hid_kb_key); + totp_type_code_worker_press_key(hid_kb_key); i++; } + if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + furi_delay_ms(30); + totp_type_code_worker_press_key(hid_enter_key); + } + furi_mutex_release(context->string_sync); } } @@ -44,9 +55,6 @@ static int32_t totp_type_code_worker_callback(void* context) { TotpBtTypeCodeWorkerContext* bt_context = context; - furi_hal_bt_start_advertising(); - bt_context->is_advertising = true; - while(true) { uint32_t flags = furi_thread_flags_wait( TotpBtTypeCodeWorkerEventStop | TotpBtTypeCodeWorkerEventType, @@ -64,15 +72,20 @@ static int32_t totp_type_code_worker_callback(void* context) { } } - furi_hal_bt_stop_advertising(); - - bt_context->is_advertising = false; - furi_mutex_free(context_mutex); return 0; } +static void connection_status_changed_callback(BtStatus status, void* context) { + TotpBtTypeCodeWorkerContext* bt_context = context; + if(status == BtStatusConnected) { + bt_context->is_connected = true; + } else if(status < BtStatusConnected) { + bt_context->is_connected = false; + } +} + void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buf, @@ -100,8 +113,10 @@ void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) { void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, - TotpBtTypeCodeWorkerEvent event) { + TotpBtTypeCodeWorkerEvent event, + uint8_t flags) { furi_assert(context != NULL); + context->flags = flags; furi_thread_flags_set(furi_thread_get_id(context->thread), event); } @@ -111,13 +126,19 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { context->bt = furi_record_open(RECORD_BT); context->is_advertising = false; + context->is_connected = false; bt_disconnect(context->bt); + furi_hal_bt_reinit(); furi_delay_ms(200); bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH); if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) { FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile"); } + furi_hal_bt_start_advertising(); + context->is_advertising = true; + bt_set_status_changed_callback(context->bt, connection_status_changed_callback, context); + return context; } @@ -128,7 +149,14 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { totp_bt_type_code_worker_stop(context); } + bt_set_status_changed_callback(context->bt, NULL, NULL); + + furi_hal_bt_stop_advertising(); + context->is_advertising = false; + context->is_connected = false; + bt_disconnect(context->bt); + furi_delay_ms(200); bt_keys_storage_set_default_path(context->bt); if(!bt_set_profile(context->bt, BtProfileSerial)) { diff --git a/totp/workers/bt_type_code/bt_type_code.h b/totp/workers/bt_type_code/bt_type_code.h index 475b66db40d..46fdb7aff82 100644 --- a/totp/workers/bt_type_code/bt_type_code.h +++ b/totp/workers/bt_type_code/bt_type_code.h @@ -10,16 +10,18 @@ typedef uint8_t TotpBtTypeCodeWorkerEvent; typedef struct { char* string; uint8_t string_length; + uint8_t flags; FuriThread* thread; FuriMutex* string_sync; Bt* bt; bool is_advertising; + bool is_connected; } TotpBtTypeCodeWorkerContext; enum TotpBtTypeCodeWorkerEvents { - TotpBtTypeCodeWorkerEventReserved = (1 << 0), - TotpBtTypeCodeWorkerEventStop = (1 << 1), - TotpBtTypeCodeWorkerEventType = (1 << 2) + TotpBtTypeCodeWorkerEventReserved = 0b0000, + TotpBtTypeCodeWorkerEventStop = 0b0100, + TotpBtTypeCodeWorkerEventType = 0b1000 }; TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); @@ -32,4 +34,5 @@ void totp_bt_type_code_worker_start( void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, - TotpBtTypeCodeWorkerEvent event); \ No newline at end of file + TotpBtTypeCodeWorkerEvent event, + uint8_t flags); \ No newline at end of file diff --git a/totp/workers/constants.c b/totp/workers/constants.c index f3c10357814..cde5582ef86 100644 --- a/totp/workers/constants.c +++ b/totp/workers/constants.c @@ -11,4 +11,6 @@ const uint8_t hid_number_keys[10] = { HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, - HID_KEYBOARD_9}; \ No newline at end of file + HID_KEYBOARD_9}; + +const uint8_t hid_enter_key = HID_KEYBOARD_RETURN; \ No newline at end of file diff --git a/totp/workers/constants.h b/totp/workers/constants.h index c314b6c166b..1ec324d3fc8 100644 --- a/totp/workers/constants.h +++ b/totp/workers/constants.h @@ -1,4 +1,5 @@ #pragma once #include -extern const uint8_t hid_number_keys[10]; \ No newline at end of file +extern const uint8_t hid_number_keys[10]; +extern const uint8_t hid_enter_key; diff --git a/totp/workers/usb_type_code/usb_type_code.c b/totp/workers/usb_type_code/usb_type_code.c index 3132e2317e5..9d2a834d375 100644 --- a/totp/workers/usb_type_code/usb_type_code.c +++ b/totp/workers/usb_type_code/usb_type_code.c @@ -1,5 +1,6 @@ #include "usb_type_code.h" #include "../../services/convert/convert.h" +#include "../../types/token_info.h" #include "../constants.h" static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { @@ -13,7 +14,14 @@ static inline bool totp_type_code_worker_stop_requested() { return furi_thread_flags_get() & TotpUsbTypeCodeWorkerEventStop; } +static void totp_type_code_worker_press_key(uint8_t key) { + furi_hal_hid_kb_press(key); + furi_delay_ms(30); + furi_hal_hid_kb_release(key); +} + static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* context) { + TokenAutomationFeature features = context->flags; context->usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); @@ -31,12 +39,15 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]); if(digit > 9) break; uint8_t hid_kb_key = hid_number_keys[digit]; - furi_hal_hid_kb_press(hid_kb_key); - furi_delay_ms(30); - furi_hal_hid_kb_release(hid_kb_key); + totp_type_code_worker_press_key(hid_kb_key); i++; } + if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + furi_delay_ms(30); + totp_type_code_worker_press_key(hid_enter_key); + } + furi_mutex_release(context->string_sync); furi_delay_ms(100); @@ -104,7 +115,9 @@ void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) { void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, - TotpUsbTypeCodeWorkerEvent event) { + TotpUsbTypeCodeWorkerEvent event, + uint8_t flags) { furi_assert(context != NULL); + context->flags = flags; furi_thread_flags_set(furi_thread_get_id(context->thread), event); } \ No newline at end of file diff --git a/totp/workers/usb_type_code/usb_type_code.h b/totp/workers/usb_type_code/usb_type_code.h index 94fddcc5963..d0ea600cefc 100644 --- a/totp/workers/usb_type_code/usb_type_code.h +++ b/totp/workers/usb_type_code/usb_type_code.h @@ -9,15 +9,16 @@ typedef uint8_t TotpUsbTypeCodeWorkerEvent; typedef struct { char* string; uint8_t string_length; + uint8_t flags; FuriThread* thread; FuriMutex* string_sync; FuriHalUsbInterface* usb_mode_prev; } TotpUsbTypeCodeWorkerContext; enum TotpUsbTypeCodeWorkerEvents { - TotpUsbTypeCodeWorkerEventReserved = (1 << 0), - TotpUsbTypeCodeWorkerEventStop = (1 << 1), - TotpUsbTypeCodeWorkerEventType = (1 << 2) + TotpUsbTypeCodeWorkerEventReserved = 0b00, + TotpUsbTypeCodeWorkerEventStop = 0b01, + TotpUsbTypeCodeWorkerEventType = 0b10 }; TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( @@ -27,4 +28,5 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context); void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, - TotpUsbTypeCodeWorkerEvent event); \ No newline at end of file + TotpUsbTypeCodeWorkerEvent event, + uint8_t flags); \ No newline at end of file