From ee4252c239211da90e47f75ee4339f9a4327c011 Mon Sep 17 00:00:00 2001 From: Alexander Kopachov Date: Thu, 17 Nov 2022 12:45:52 +0100 Subject: [PATCH] Implemented #26 (#27) * Implemented #26 * Improved HID worker shutdown code * Refactoring --- scenes/app_settings/totp_app_settings.c | 2 +- .../totp_scene_generate_token.c | 2 +- scenes/scene_director.h | 34 ++++ scenes/token_menu/totp_scene_token_menu.c | 13 +- scenes/totp_scenes_enum.h | 26 +++ services/base32/base32.c | 2 - services/base32/base32.h | 10 +- services/cli/cli.c | 5 + services/cli/commands/add/add.c | 2 + services/cli/commands/help/help.c | 4 + services/cli/commands/move/move.c | 164 ++++++++++++++++++ services/cli/commands/move/move.h | 12 ++ services/config/config.h | 59 +++++++ services/crypto/crypto.h | 30 ++++ services/crypto/memset_s.h | 9 +- services/hid_worker/hid_worker.c | 14 +- services/hmac/byteswap.h | 11 ++ services/list/list.c | 59 ++++++- services/list/list.h | 80 +++++++-- services/roll_value/roll_value.h | 41 ++++- services/timezone_utils/timezone_utils.h | 12 ++ services/totp/totp.c | 63 +++---- services/totp/totp.h | 54 +++--- services/ui/ui_controls.h | 28 +++ types/plugin_state.h | 60 +++++++ types/token_info.h | 81 ++++++++- 26 files changed, 780 insertions(+), 97 deletions(-) create mode 100644 services/cli/commands/move/move.c create mode 100644 services/cli/commands/move/move.h diff --git a/scenes/app_settings/totp_app_settings.c b/scenes/app_settings/totp_app_settings.c index 63db2e46bf5..84f74915944 100644 --- a/scenes/app_settings/totp_app_settings.c +++ b/scenes/app_settings/totp_app_settings.c @@ -101,7 +101,7 @@ bool totp_scene_app_settings_handle_event( } SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(event->input.type != InputTypePress) { + if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) { return true; } diff --git a/scenes/generate_token/totp_scene_generate_token.c b/scenes/generate_token/totp_scene_generate_token.c index e2d264caee1..266c04736ed 100644 --- a/scenes/generate_token/totp_scene_generate_token.c +++ b/scenes/generate_token/totp_scene_generate_token.c @@ -293,7 +293,7 @@ bool totp_scene_generate_token_handle_event( return true; } - if(event->input.type != InputTypePress) { + if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) { return true; } diff --git a/scenes/scene_director.h b/scenes/scene_director.h index cc06029d39c..541a63f1c45 100644 --- a/scenes/scene_director.h +++ b/scenes/scene_director.h @@ -5,12 +5,46 @@ #include "../types/plugin_event.h" #include "totp_scenes_enum.h" +/** + * @brief Activates scene + * @param plugin_state application state + * @param scene scene to be activated + * @param context scene context to be passed to the scene activation method + */ void totp_scene_director_activate_scene( PluginState* const plugin_state, Scene scene, const void* context); + +/** + * @brief Deactivate current scene + * @param plugin_state application state + */ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state); + +/** + * @brief Initializes all the available scenes + * @param plugin_state application state + */ void totp_scene_director_init_scenes(PluginState* const plugin_state); + +/** + * @brief Renders current scene + * @param canvas canvas to render at + * @param plugin_state application state + */ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state); + +/** + * @brief Disposes all the available scenes + * @param plugin_state application state + */ void totp_scene_director_dispose(const PluginState* const plugin_state); + +/** + * @brief Handles application event for the current scene + * @param event event to be handled + * @param plugin_state application state + * @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); diff --git a/scenes/token_menu/totp_scene_token_menu.c b/scenes/token_menu/totp_scene_token_menu.c index d936bf95fdd..d4a837a6981 100644 --- a/scenes/token_menu/totp_scene_token_menu.c +++ b/scenes/token_menu/totp_scene_token_menu.c @@ -147,13 +147,14 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt dialog_message_free(message); if(dialog_result == DialogMessageButtonRight && !scene_state->current_token_index.is_null) { - ListNode* list_node = list_element_at( - plugin_state->tokens_list, scene_state->current_token_index.value); - - TokenInfo* tokenInfo = list_node->data; - token_info_free(tokenInfo); - plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node); + TokenInfo* tokenInfo = NULL; + plugin_state->tokens_list = list_remove_at( + plugin_state->tokens_list, + scene_state->current_token_index.value, + (void**)&tokenInfo); plugin_state->tokens_count--; + furi_check(tokenInfo != NULL); + token_info_free(tokenInfo); totp_full_save_config_file(plugin_state); totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); diff --git a/scenes/totp_scenes_enum.h b/scenes/totp_scenes_enum.h index c2b153a0290..c90f76bab37 100644 --- a/scenes/totp_scenes_enum.h +++ b/scenes/totp_scenes_enum.h @@ -1,10 +1,36 @@ #pragma once +/** + * @brief TOTP application scenes + */ typedef enum { + /** + * @brief Empty scene which does nothing + */ TotpSceneNone, + + /** + * @brief Scene where user have to enter PIN to authenticate + */ TotpSceneAuthentication, + + /** + * @brief Scene where actual TOTP token is getting generated and displayed to the user + */ TotpSceneGenerateToken, + + /** + * @brief Scene where user can add new token + */ TotpSceneAddNewToken, + + /** + * @brief Scene with a menu for given token, allowing user to do multiple actions + */ TotpSceneTokenMenu, + + /** + * @brief Scene where user can change application settings + */ TotpSceneAppSettings } Scene; diff --git a/services/base32/base32.c b/services/base32/base32.c index 943da6b2a66..9781c831fd6 100644 --- a/services/base32/base32.c +++ b/services/base32/base32.c @@ -15,8 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - #include "base32.h" int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) { diff --git a/services/base32/base32.h b/services/base32/base32.h index 1d7736e81c0..dea1a1c8197 100644 --- a/services/base32/base32.h +++ b/services/base32/base32.h @@ -29,5 +29,11 @@ #include -int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) - __attribute__((visibility("hidden"))); +/** + * @brief Decodes Base-32 encoded bytes into plain bytes. + * @param encoded Base-32 encoded bytes + * @param[out] result result output buffer + * @param bufSize result output buffer size + * @return Decoded result length in bytes if successfully decoded; \c -1 otherwise + */ +int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize); diff --git a/services/cli/cli.c b/services/cli/cli.c index 76e58a02db7..2cfae3f1520 100644 --- a/services/cli/cli.c +++ b/services/cli/cli.c @@ -8,6 +8,7 @@ #include "commands/delete/delete.h" #include "commands/timezone/timezone.h" #include "commands/help/help.h" +#include "commands/move/move.h" static void totp_cli_print_unknown_command(const FuriString* unknown_command) { TOTP_CLI_PRINTF( @@ -44,6 +45,10 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_TIMEZONE) == 0 || furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_TIMEZONE_ALT) == 0) { totp_cli_command_timezone_handle(plugin_state, args, cli); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_MOVE) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_MOVE_ALT) == 0) { + totp_cli_command_move_handle(plugin_state, args, cli); } else { totp_cli_print_unknown_command(cmd); } diff --git a/services/cli/commands/add/add.c b/services/cli/commands/add/add.c index 2e8f225474e..4b7aaf32716 100644 --- a/services/cli/commands/add/add.c +++ b/services/cli/commands/add/add.c @@ -175,6 +175,8 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl } 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("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); } if(!parsed) { diff --git a/services/cli/commands/help/help.c b/services/cli/commands/help/help.c index ab592fbba2b..432f0ab71f8 100644 --- a/services/cli/commands/help/help.c +++ b/services/cli/commands/help/help.c @@ -4,6 +4,7 @@ #include "../delete/delete.h" #include "../list/list.h" #include "../timezone/timezone.h" +#include "../move/move.h" void totp_cli_command_help_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT @@ -23,6 +24,7 @@ void totp_cli_command_help_handle() { totp_cli_command_add_docopt_usage(); totp_cli_command_delete_docopt_usage(); totp_cli_command_timezone_docopt_usage(); + totp_cli_command_move_docopt_usage(); cli_nl(); TOTP_CLI_PRINTF("Commands:\r\n"); totp_cli_command_help_docopt_commands(); @@ -30,6 +32,7 @@ void totp_cli_command_help_handle() { totp_cli_command_add_docopt_commands(); totp_cli_command_delete_docopt_commands(); totp_cli_command_timezone_docopt_commands(); + totp_cli_command_move_docopt_commands(); cli_nl(); TOTP_CLI_PRINTF("Arguments:\r\n"); totp_cli_command_add_docopt_arguments(); @@ -39,4 +42,5 @@ void totp_cli_command_help_handle() { TOTP_CLI_PRINTF("Options:\r\n"); totp_cli_command_add_docopt_options(); totp_cli_command_delete_docopt_options(); + totp_cli_command_move_docopt_options(); } \ No newline at end of file diff --git a/services/cli/commands/move/move.c b/services/cli/commands/move/move.c new file mode 100644 index 00000000000..9ed0a604ad1 --- /dev/null +++ b/services/cli/commands/move/move.c @@ -0,0 +1,164 @@ +#include "move.h" + +#include +#include +#include "../../../list/list.h" +#include "../../../../types/token_info.h" +#include "../../../config/config.h" +#include "../../cli_helpers.h" +#include "../../../../scenes/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" + +#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"); +} + +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_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"); +} + +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) { + 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( + "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(!args_read_int_and_trim(args, &new_token_index)) { + TOTP_CLI_PRINTF( + "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX + "\"\r\n"); + } else if(new_token_index < 1 || new_token_index > plugin_state->tokens_count) { + TOTP_CLI_PRINTF( + "\"%" PRId16 + "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX + "\"\r\n", + new_token_index); + } else { + parsed = true; + } + } else { + TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + } + + 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; + } + + bool activate_generate_token_scene = false; + if(plugin_state->current_scene != TotpSceneAuthentication) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + activate_generate_token_scene = true; + } + + bool token_updated = false; + TokenInfo* token_info = NULL; + if(new_token_index > 0) { + plugin_state->tokens_list = + list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info); + furi_check(token_info != NULL); + plugin_state->tokens_list = + list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info); + token_updated = true; + } else { + 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) { + totp_full_save_config_file(plugin_state); + } + + if(activate_generate_token_scene) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + + if(token_updated) { + TOTP_CLI_PRINTF("Token \"%s\" has been successfully updated\r\n", token_info->name); + } else { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + } + + furi_string_free(temp_str); +} \ No newline at end of file diff --git a/services/cli/commands/move/move.h b/services/cli/commands/move/move.h new file mode 100644 index 00000000000..b06a716794b --- /dev/null +++ b/services/cli/commands/move/move.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "../../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_MOVE "move" +#define TOTP_CLI_COMMAND_MOVE_ALT "mv" + +void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_move_docopt_commands(); +void totp_cli_command_move_docopt_usage(); +void totp_cli_command_move_docopt_options(); \ No newline at end of file diff --git a/services/config/config.h b/services/config/config.h index d452ad4b3f7..cf433f9e595 100644 --- a/services/config/config.h +++ b/services/config/config.h @@ -6,18 +6,77 @@ #include "../../types/token_info.h" #include "constants.h" +/** + * @brief Token loading results + */ typedef enum { + /** + * @brief All the tokens loaded successfully + */ TokenLoadingResultSuccess, + + /** + * @brief All the tokens loaded, but there are some warnings + */ TokenLoadingResultWarning, + + /** + * @brief Tokens not loaded because of error(s) + */ TokenLoadingResultError } TokenLoadingResult; +/** + * @brief Opens storage record + * @return Storage record + */ Storage* totp_open_storage(); + +/** + * @brief Closes storage record + */ void totp_close_storage(); + +/** + * @brief Opens or creates TOTP application standard config file + * @param storage storage record to use + * @return Config file reference + */ FlipperFormat* totp_open_config_file(Storage* storage); + +/** + * @brief Closes config file + * @param file config file reference + */ void totp_close_config_file(FlipperFormat* file); + +/** + * @brief Saves all the settings and tokens to an application config file + * @param plugin_state application state + */ void totp_full_save_config_file(const PluginState* const plugin_state); + +/** + * @brief Loads basic information from an application config file into application state without loading all the tokens + * @param plugin_state application state + */ void totp_config_file_load_base(PluginState* const plugin_state); + +/** + * @brief Loads tokens from an application config file into application state + * @param plugin_state application state + * @return Results of the loading + */ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); + +/** + * @brief Add new token to the end of the application config file + * @param token_info token information to be saved + */ void totp_config_file_save_new_token(const TokenInfo* token_info); + +/** + * @brief Updates timezone offset in an application config file + * @param new_timezone_offset new timezone offset to be set + */ void totp_config_file_update_timezone_offset(float new_timezone_offset); diff --git a/services/crypto/crypto.h b/services/crypto/crypto.h index f0a28f7985f..d39fe013bee 100644 --- a/services/crypto/crypto.h +++ b/services/crypto/crypto.h @@ -2,15 +2,45 @@ #include "../../types/plugin_state.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 iv initialization vector (IV) to be used to encrypt plain data + * @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, 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[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, size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param plugin_state application state + * @param pin user's PIN + * @param pin_length user's PIN length + */ void totp_crypto_seed_iv(PluginState* plugin_state, 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 + * @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 diff --git a/services/crypto/memset_s.h b/services/crypto/memset_s.h index 060c6642b47..54628860d08 100644 --- a/services/crypto/memset_s.h +++ b/services/crypto/memset_s.h @@ -2,7 +2,6 @@ #include #include -#include #ifndef _RSIZE_T_DECLARED typedef uint64_t rsize_t; @@ -13,4 +12,12 @@ typedef int16_t errno_t; //-V677 #define _ERRNOT_DECLARED #endif +/** + * @brief Copies the value \p c into each of the first \p n characters of the object pointed to by \p s. + * @param s pointer to the object to fill + * @param smax size of the destination object + * @param c fill byte + * @param n number of bytes to fill + * @return \c 0 on success; non-zero otherwise + */ errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n); \ No newline at end of file diff --git a/services/hid_worker/hid_worker.c b/services/hid_worker/hid_worker.c index f57a64eb106..538fdbe97ca 100644 --- a/services/hid_worker/hid_worker.c +++ b/services/hid_worker/hid_worker.c @@ -1,6 +1,6 @@ #include "hid_worker.h" -const uint8_t hid_number_keys[10] = { +static const uint8_t hid_number_keys[10] = { HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, @@ -19,6 +19,10 @@ static void totp_hid_worker_restore_usb_mode(TotpHidWorkerTypeContext* context) } } +static inline bool totp_hid_worker_stop_requested() { + return furi_thread_flags_get() & TotpHidWorkerEvtStop; +} + static void totp_hid_worker_type_code(TotpHidWorkerTypeContext* context) { context->usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_unlock(); @@ -27,11 +31,11 @@ static void totp_hid_worker_type_code(TotpHidWorkerTypeContext* context) { do { furi_delay_ms(500); i++; - } while(!furi_hal_hid_is_connected() && i < 50); - furi_delay_ms(500); + } while(!furi_hal_hid_is_connected() && i < 100 && !totp_hid_worker_stop_requested()); if(furi_hal_hid_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 = context->string[i] - '0'; @@ -91,7 +95,7 @@ TotpHidWorkerTypeContext* totp_hid_worker_start() { } void totp_hid_worker_stop(TotpHidWorkerTypeContext* context) { - furi_assert(context); + furi_assert(context != NULL); furi_thread_flags_set(furi_thread_get_id(context->thread), TotpHidWorkerEvtStop); furi_thread_join(context->thread); furi_thread_free(context->thread); @@ -101,6 +105,6 @@ void totp_hid_worker_stop(TotpHidWorkerTypeContext* context) { } void totp_hid_worker_notify(TotpHidWorkerTypeContext* context, TotpHidWorkerEvtFlags event) { - furi_assert(context); + furi_assert(context != NULL); furi_thread_flags_set(furi_thread_get_id(context->thread), event); } \ No newline at end of file diff --git a/services/hmac/byteswap.h b/services/hmac/byteswap.h index 411f3d5c436..2e3f1743f07 100644 --- a/services/hmac/byteswap.h +++ b/services/hmac/byteswap.h @@ -2,5 +2,16 @@ #include +/** + * @brief Swap bytes in 32-bit value + * @param val value to swap bytes in + * @return Value with bytes swapped + */ uint32_t swap_uint32(uint32_t val); + +/** + * @brief Swap bytes in 64-bit value + * @param val value to swap bytes in + * @return Value with bytes swapped + */ uint64_t swap_uint64(uint64_t val); diff --git a/services/list/list.c b/services/list/list.c index bc7307fdbaf..f7abb6c8ef9 100644 --- a/services/list/list.c +++ b/services/list/list.c @@ -29,7 +29,7 @@ ListNode* list_add(ListNode* head, void* data) { } ListNode* list_find(ListNode* head, const void* data) { - ListNode* it; + ListNode* it = NULL; for(it = head; it != NULL; it = it->next) if(it->data == data) break; @@ -67,6 +67,63 @@ ListNode* list_remove(ListNode* head, ListNode* ep) { return head; } +ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data) { + if(head == NULL) { + return NULL; + } + + ListNode* it; + ListNode* prev = NULL; + + uint16_t i; + + for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) + ; + + if(it == NULL) return head; + + ListNode* new_head = head; + if(prev == NULL) { + new_head = it->next; + } else { + prev->next = it->next; + } + + if(removed_node_data != NULL) { + *removed_node_data = it->data; + } + + free(it); + + return new_head; +} + +ListNode* list_insert_at(ListNode* head, uint16_t index, void* data) { + if(index == 0 || head == NULL) { + ListNode* new_head = list_init_head(data); + if(new_head != NULL) { + new_head->next = head; + } + return new_head; + } + + ListNode* it; + ListNode* prev = NULL; + + uint16_t i; + + for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) + ; + + ListNode* new = malloc(sizeof(ListNode)); + if(new == NULL) return NULL; + new->data = data; + new->next = it; + prev->next = new; + + return head; +} + void list_free(ListNode* head) { ListNode* it = head; ListNode* tmp; diff --git a/services/list/list.h b/services/list/list.h index 7721b252224..715c749012d 100644 --- a/services/list/list.h +++ b/services/list/list.h @@ -3,25 +3,85 @@ #include #include +/** + * @brief Single linked list node + */ typedef struct ListNode { + /** + * @brief Pointer to the data assigned to the current list node + */ void* data; + + /** + * @brief Pointer to the next list node + */ struct ListNode* next; } ListNode; +/** + * @brief Initializes a new list node head + * @param data data to be assigned to the head list node + * @return Head list node + */ ListNode* list_init_head(void* data); + +/** + * @brief Adds new list node to the end of the list + * @param head head list node + * @param data data to be assigned to the newly added list node + * @return Head list node + */ ListNode* list_add( ListNode* head, void* data); /* adds element with specified data to the end of the list and returns new head node. */ -ListNode* list_find( - ListNode* head, - const void* data); /* returns pointer of element with specified data in list. */ -ListNode* list_element_at( - ListNode* head, - uint16_t index); /* returns pointer of element with specified index in list. */ -ListNode* list_remove( - ListNode* head, - ListNode* ep); /* removes element from the list and returns new head node. */ -void list_free(ListNode* head); /* deletes all elements of the list. */ + +/** + * @brief Searches list node with the given assigned \p data in the list + * @param head head list node + * @param data data to be searched + * @return List node containing \p data if there is such a node in the list; \c NULL otherwise + */ +ListNode* list_find(ListNode* head, const void* data); + +/** + * @brief Searches list node with the given \p index in the list + * @param head head list node + * @param index desired list node index + * @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise + */ +ListNode* list_element_at(ListNode* head, uint16_t index); + +/** + * @brief Removes list node from the list + * @param head head list node + * @param ep list node to be removed + * @return Head list node + */ +ListNode* list_remove(ListNode* head, ListNode* ep); + +/** + * @brief Removes list node with the given \p index in the list from the list + * @param head head list node + * @param index index of the node to be removed + * @param[out] removed_node_data data which was assigned to the removed list node + * @return Head list node + */ +ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data); + +/** + * @brief Inserts new list node at the given index + * @param head head list node + * @param index index in the list where the new list node should be inserted + * @param data data to be assgned to the new list node + * @return Head list node + */ +ListNode* list_insert_at(ListNode* head, uint16_t index, void* data); + +/** + * @brief Disposes all the list nodes in the list + * @param head head list node + */ +void list_free(ListNode* head); #define TOTP_LIST_INIT_OR_ADD(head, item, assert) \ do { \ diff --git a/services/roll_value/roll_value.h b/services/roll_value/roll_value.h index 87337597f07..bb8360c9656 100644 --- a/services/roll_value/roll_value.h +++ b/services/roll_value/roll_value.h @@ -2,7 +2,17 @@ #include -typedef enum { RollOverflowBehaviorStop, RollOverflowBehaviorRoll } TotpRollValueOverflowBehavior; +typedef enum { + /** + * @brief Do not change value if it reached constraint + */ + RollOverflowBehaviorStop, + + /** + * @brief Set value to opposite constraint value if it reached constraint + */ + RollOverflowBehaviorRoll +} TotpRollValueOverflowBehavior; #define TOTP_ROLL_VALUE_FN_HEADER(type, step_type) \ void totp_roll_value_##type( \ @@ -12,6 +22,35 @@ typedef enum { RollOverflowBehaviorStop, RollOverflowBehaviorRoll } TotpRollValu type max, \ TotpRollValueOverflowBehavior overflow_behavior) +/** + * @brief Rolls \c int8_t \p value using \p min and \p max as an value constraints with \p step step. + * When value reaches constraint value \p overflow_behavior defines what to do next. + * @param[in,out] value value to roll + * @param step step to be used to change value + * @param min minimal possible value + * @param max maximum possible value + * @param overflow_behavior defines what to do when value reaches constraint value + */ TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t); + +/** + * @brief Rolls \c uint8_t \p value using \p min and \p max as an value constraints with \p step step. + * When value reaches constraint value \p overflow_behavior defines what to do next. + * @param[in,out] value value to roll + * @param step step to be used to change value + * @param min minimal possible value + * @param max maximum possible value + * @param overflow_behavior defines what to do when value reaches constraint value + */ TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); + +/** + * @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step. + * When value reaches constraint value \p overflow_behavior defines what to do next. + * @param[in,out] value value to roll + * @param step step to be used to change value + * @param min minimal possible value + * @param max maximum possible value + * @param overflow_behavior defines what to do when value reaches constraint value + */ TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t); \ No newline at end of file diff --git a/services/timezone_utils/timezone_utils.h b/services/timezone_utils/timezone_utils.h index 0ae829d7462..5bb3b8eada2 100644 --- a/services/timezone_utils/timezone_utils.h +++ b/services/timezone_utils/timezone_utils.h @@ -2,5 +2,17 @@ #include +/** + * @brief Calculates timezone offset in seconds given timezone offset in hours. + * @param hours timezone offset in hours + * @return Timezone offset in seconds. + */ int32_t timezone_offset_from_hours(float hours); + +/** + * @brief Applies timezone offset to a given time. + * @param time time to apply offset to. + * @param offset timezone offset in seconds. + * @return Time with timezone offset applied. + */ uint64_t timezone_offset_apply(uint64_t time, int32_t offset); diff --git a/services/totp/totp.c b/services/totp/totp.c index 9e0672ea95b..0816d1812c0 100644 --- a/services/totp/totp.c +++ b/services/totp/totp.c @@ -11,48 +11,43 @@ #include "../hmac/byteswap.h" #include "../timezone_utils/timezone_utils.h" -/* - Generates the timeblock for a time in seconds. - - Timeblocks are the amount of intervals in a given time. For example, - if 1,000,000 seconds has passed for 30 second intervals, you would get - 33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds. - - for_time is a time in seconds to get the current timeblocks - - Returns - timeblock given for_time, using data->interval - error, 0 -*/ +#define HMAC_MAX_SIZE 64 + +/** + * @brief Generates the timeblock for a time in seconds. + * Timeblocks are the amount of intervals in a given time. For example, + * if 1,000,000 seconds has passed for 30 second intervals, you would get + * 33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds. + * @param interval in seconds + * @param for_time a time in seconds to get the current timeblocks + * @return Timeblock given \p for_time using \p interval + */ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) { return for_time / interval; } -/* - Generates an OTP (One Time Password). - - input is a number used to generate the OTP - out_str is the null-terminated output string already allocated - - Returns - OTP code if otp code was successfully generated - 0 otherwise -*/ +/** + * @brief Generates an OTP (One Time Password) + * @param algo hashing algorithm to be used + * @param digits desired TOTP code length + * @param plain_secret plain token secret + * @param plain_secret_length plain token secret length + * @param input input data for OTP code generation + * @return OTP code if code was successfully generated; 0 otherwise + */ uint32_t otp_generate( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, size_t plain_secret_length, uint64_t input) { - uint8_t* hmac = malloc(64); - if(hmac == NULL) return OTP_ERROR; - memset(hmac, 0, 64); + uint8_t hmac[HMAC_MAX_SIZE] = {0}; uint64_t input_swapped = swap_uint64(input); - int hmac_len = (*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, hmac); + int hmac_len = + (*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, &hmac[0]); if(hmac_len == 0) { - free(hmac); return OTP_ERROR; } @@ -62,21 +57,9 @@ uint32_t otp_generate( (hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF)); i_code %= (uint64_t)pow(10, digits); - free(hmac); return i_code; } -/* - Generates a OTP key using the totp algorithm. - - for_time is the time the generated key will be created for - offset is a timeblock adjustment for the generated key - out_str is the null-terminated output string already allocated - - Returns - TOTP code if otp code was successfully generated - 0 otherwise -*/ uint32_t totp_at( TOTP_ALGO algo, uint8_t digits, diff --git a/services/totp/totp.h b/services/totp/totp.h index 431ca11aafe..3f45a022370 100644 --- a/services/totp/totp.h +++ b/services/totp/totp.h @@ -5,16 +5,15 @@ #define OTP_ERROR (0) -/* - Must compute HMAC using passed arguments, - output as char array through output. - - key is secret key. - input is input number. - output is an output buffer of the resulting HMAC operation. - - Must return 0 if error, or the length in bytes of the HMAC operation. -*/ +/** + * @brief Must compute HMAC using passed arguments, output as char array through output. + * \p key is secret key buffer. + * \p key_length is secret key buffer length. + * \p input is input buffer. + * \p input_length is input buffer length. + * \p output is an output buffer of the resulting HMAC operation. + * Must return 0 if error, or the length in bytes of the HMAC operation. + */ typedef int (*TOTP_ALGO)( const uint8_t* key, size_t key_length, @@ -22,27 +21,32 @@ typedef int (*TOTP_ALGO)( size_t input_length, uint8_t* output); -/* - Computes HMAC using SHA1 -*/ +/** + * @brief Computes HMAC using SHA1 + */ extern const TOTP_ALGO TOTP_ALGO_SHA1; -/* - Computes HMAC using SHA256 -*/ +/** + * @brief Computes HMAC using SHA256 + */ extern const TOTP_ALGO TOTP_ALGO_SHA256; -/* - Computes HMAC using SHA512 -*/ +/** + * @brief Computes HMAC using SHA512 + */ extern const TOTP_ALGO TOTP_ALGO_SHA512; -/* - Computes TOTP token - Returns: - TOTP token on success - 0 otherwise -*/ +/** + * @brief Generates a OTP key using the totp algorithm. + * @param algo hashing algorithm to be used + * @param digits desired TOTP code length + * @param plain_secret plain token secret + * @param plain_secret_length plain token secret length + * @param for_time the time the generated key will be created for + * @param timezone UTC timezone adjustment for the generated key + * @param interval token lifetime in seconds + * @return TOTP code if code was successfully generated; 0 otherwise + */ uint32_t totp_at( TOTP_ALGO algo, uint8_t digits, diff --git a/services/ui/ui_controls.h b/services/ui/ui_controls.h index ef3af5f5545..b97006a03c5 100644 --- a/services/ui/ui_controls.h +++ b/services/ui/ui_controls.h @@ -3,11 +3,29 @@ #include #include +/** + * @brief Renders TextBox control + * @param canvas canvas to render control at + * @param y vertical position of a control to be rendered at + * @param text text to be rendered inside control + * @param is_selected whether control should be rendered as focused or not + */ void ui_control_text_box_render( Canvas* const canvas, int16_t y, const char* text, bool is_selected); + +/** + * @brief Renders Button control + * @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 width control width + * @param height control height + * @param text text to be rendered inside control + * @param is_selected whether control should be rendered as focused or not + */ void ui_control_button_render( Canvas* const canvas, int16_t x, @@ -16,6 +34,16 @@ void ui_control_button_render( uint8_t height, const char* text, bool is_selected); + +/** + * @brief Renders Select control + * @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 width control width + * @param text text to be rendered inside control + * @param is_selected whether control should be rendered as focused or not + */ void ui_control_select_render( Canvas* const canvas, int16_t x, diff --git a/types/plugin_state.h b/types/plugin_state.h index 9b16d7d57b8..637f9916f77 100644 --- a/types/plugin_state.h +++ b/types/plugin_state.h @@ -8,22 +8,82 @@ #define TOTP_IV_SIZE 16 +/** + * @brief Application state structure + */ typedef struct { + /** + * @brief Application current scene + */ Scene current_scene; + + /** + * @brief Application current scene state + */ void* current_scene_state; + + /** + * @brief Whether scene is changing now + */ bool changing_scene; + + /** + * @brief Reference to the firmware notification subsystem + */ NotificationApp* notification; + + /** + * @brief Reference to the firmware dialogs subsystem + */ DialogsApp* dialogs; + + /** + * @brief Reference to the firmware GUI subsystem + */ Gui* gui; + /** + * @brief Timezone UTC offset in hours + */ float timezone_offset; + + /** + * @brief Token list head node + */ ListNode* tokens_list; + + /** + * @brief Whether token list is loaded or not + */ bool token_list_loaded; + + /** + * @brief Tokens list length + */ uint16_t tokens_count; + /** + * @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]; } PluginState; diff --git a/types/token_info.h b/types/token_info.h index 6c55e095fcf..cbcf0ddefdb 100644 --- a/types/token_info.h +++ b/types/token_info.h @@ -2,25 +2,102 @@ #include -typedef enum { SHA1, SHA256, SHA512 } TokenHashAlgo; +/** + * @brief Hashing algorithm to be used to generate token + */ +typedef enum { + /** + * @brief SHA1 hashing algorithm + */ + SHA1, -typedef enum { TOTP_6_DIGITS, TOTP_8_DIGITS } TokenDigitsCount; + /** + * @brief SHA256 hashing algorithm + */ + SHA256, + + /** + * @brief SHA512 hashing algorithm + */ + SHA512 +} TokenHashAlgo; + +/** + * @brief Token digits count to be generated. + */ +typedef enum { + /** + * @brief 6 digits + */ + TOTP_6_DIGITS, + + /** + * @brief 8 digits + */ + TOTP_8_DIGITS +} TokenDigitsCount; #define TOTP_TOKEN_DIGITS_MAX_COUNT 8 +/** + * @brief TOTP token information + */ typedef struct { + /** + * @brief Encrypted token secret + */ uint8_t* token; + + /** + * @brief Encrypted token secret length + */ size_t token_length; + + /** + * @brief User-friendly token name + */ char* name; + + /** + * @brief Hashing algorithm + */ TokenHashAlgo algo; + + /** + * @brief Desired TOTP token length + */ TokenDigitsCount digits; } TokenInfo; +/** + * @brief Allocates a new instance of \c TokenInfo + * @return + */ TokenInfo* token_info_alloc(); + +/** + * @brief Disposes all the resources allocated by the given \c TokenInfo instance + * @param token_info instance to be disposed + */ void token_info_free(TokenInfo* token_info); + +/** + * @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo + * @param token_info instance where secret should be updated + * @param base32_token_secret plain token secret in Base32 format + * @param token_secret_length plain token secret length + * @param iv initialization vecor (IV) to be used for encryption + * @return \c true if token successfully set; \c false otherwise + */ bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, size_t token_secret_length, const uint8_t* iv); + +/** + * @brief Gets token digits count as \c uint8_t type + * @param token_info instance which's desired digits count should be returned + * @return Token digits length as \c uint8_t type + */ uint8_t token_info_get_digits_count(const TokenInfo* token_info);