diff --git a/scenes/add_new_token/totp_scene_add_new_token.c b/scenes/add_new_token/totp_scene_add_new_token.c index 50ee3121c0c..fc39b66cd46 100644 --- a/scenes/add_new_token/totp_scene_add_new_token.c +++ b/scenes/add_new_token/totp_scene_add_new_token.c @@ -227,31 +227,47 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState break; case ConfirmButton: { TokenInfo* tokenInfo = token_info_alloc(); - tokenInfo->name = malloc(scene_state->token_name_length + 1); - strcpy(tokenInfo->name, scene_state->token_name); - - token_info_set_secret( + bool token_secret_set = token_info_set_secret( tokenInfo, scene_state->token_secret, scene_state->token_secret_length, &plugin_state->iv[0]); - tokenInfo->algo = scene_state->algo; - tokenInfo->digits = scene_state->digits_count; + if(token_secret_set) { + tokenInfo->name = malloc(scene_state->token_name_length + 1); + strcpy(tokenInfo->name, scene_state->token_name); + tokenInfo->algo = scene_state->algo; + tokenInfo->digits = scene_state->digits_count; - if(plugin_state->tokens_list == NULL) { - plugin_state->tokens_list = list_init_head(tokenInfo); - } else { - list_add(plugin_state->tokens_list, tokenInfo); - } - plugin_state->tokens_count++; + if(plugin_state->tokens_list == NULL) { + plugin_state->tokens_list = list_init_head(tokenInfo); + } else { + list_add(plugin_state->tokens_list, tokenInfo); + } + plugin_state->tokens_count++; - totp_config_file_save_new_token(tokenInfo); + totp_config_file_save_new_token(tokenInfo); - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = plugin_state->tokens_count - 1}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = plugin_state->tokens_count - 1}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + token_info_free(tokenInfo); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_text( + message, + "Token secret is invalid", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + scene_state->selected_control = TokenSecretTextBox; + update_screen_y_offset(scene_state); + } break; } } diff --git a/scenes/generate_token/totp_scene_generate_token.c b/scenes/generate_token/totp_scene_generate_token.c index 36806e50eea..df5a3e74693 100644 --- a/scenes/generate_token/totp_scene_generate_token.c +++ b/scenes/generate_token/totp_scene_generate_token.c @@ -87,7 +87,8 @@ void update_totp_params(PluginState* const plugin_state) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; if(scene_state->current_token_index < plugin_state->tokens_count) { - TokenInfo* tokenInfo = list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; + TokenInfo* tokenInfo = + list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; scene_state->need_token_update = true; scene_state->last_code_name = tokenInfo->name; @@ -102,7 +103,31 @@ void totp_scene_generate_token_activate( PluginState* plugin_state, const GenerateTokenSceneContext* context) { if(!plugin_state->token_list_loaded) { - totp_config_file_load_tokens(plugin_state); + TokenLoadingResult token_load_result = totp_config_file_load_tokens(plugin_state); + if(token_load_result != TokenLoadingResultSuccess) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, NULL, "Okay", NULL); + if(token_load_result == TokenLoadingResultWarning) { + dialog_message_set_text( + message, + "Unable to load some tokens\nPlease review conf file", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + } else if(token_load_result == TokenLoadingResultError) { + dialog_message_set_text( + message, + "Unable to load tokens\nPlease review conf file", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + } + + dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + } } SceneState* scene_state = malloc(sizeof(SceneState)); if(context == NULL) { @@ -154,23 +179,27 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ plugin_state->tokens_list, scene_state->current_token_index) ->data); - uint8_t key_length; - uint8_t* key = totp_crypto_decrypt( - tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); - - i_token_to_str( - totp_at( - get_totp_algo_impl(tokenInfo->algo), - token_info_get_digits_count(tokenInfo), - key, - key_length, - curr_ts, - plugin_state->timezone_offset, - TOKEN_LIFETIME), - scene_state->last_code, - tokenInfo->digits); - memset(key, 0, key_length); - free(key); + if(tokenInfo->token != NULL && tokenInfo->token_length > 0) { + uint8_t key_length; + uint8_t* key = totp_crypto_decrypt( + tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); + + i_token_to_str( + totp_at( + get_totp_algo_impl(tokenInfo->algo), + token_info_get_digits_count(tokenInfo), + key, + key_length, + curr_ts, + plugin_state->timezone_offset, + TOKEN_LIFETIME), + scene_state->last_code, + tokenInfo->digits); + memset(key, 0, key_length); + free(key); + } else { + i_token_to_str(0, scene_state->last_code, tokenInfo->digits); + } if(is_new_token_time) { notification_message(plugin_state->notification, &sequence_short_vibro_and_sound); diff --git a/services/base32/base32.c b/services/base32/base32.c index d7a057677cb..943da6b2a66 100644 --- a/services/base32/base32.c +++ b/services/base32/base32.c @@ -16,7 +16,6 @@ // limitations under the License. #include -#include #include "base32.h" @@ -26,11 +25,9 @@ int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) { int count = 0; for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) { uint8_t ch = *ptr; - bool chIsValid = (ch >= '0' && ch <= '8') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); - if(!chIsValid) { + if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') { continue; } - buffer <<= 5; // Deal with commonly mistyped characters diff --git a/services/config/config.c b/services/config/config.c index b7eb5fdd12b..9a72f9ca8c9 100644 --- a/services/config/config.c +++ b/services/config/config.c @@ -155,8 +155,15 @@ FlipperFormat* totp_open_config_file(Storage* storage) { void totp_config_file_save_new_token_i(FlipperFormat* file, TokenInfo* token_info) { flipper_format_seek_to_end(file); flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name); + bool token_is_valid = token_info->token != NULL && token_info->token_length > 0; + if(!token_is_valid) { + flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!"); + } flipper_format_write_hex( file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length); + if(!token_is_valid) { + flipper_format_write_comment_cstr(file, "!!! WARNING END !!!"); + } flipper_format_write_string_cstr( file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info)); uint32_t digits_count_as_uint32 = token_info_get_digits_as_int(token_info); @@ -312,7 +319,7 @@ void totp_config_file_load_base(PluginState* const plugin_state) { totp_close_storage(); } -void totp_config_file_load_tokens(PluginState* const plugin_state) { +TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file = totp_open_config_file(storage); @@ -322,9 +329,10 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); furi_string_free(temp_str); - return; + return TokenLoadingResultError; } + TokenLoadingResult result = TokenLoadingResultSuccess; uint8_t index = 0; bool has_any_plain_secret = false; @@ -342,48 +350,61 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { uint32_t secret_bytes_count; if(!flipper_format_get_value_count( fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { - token_info_free(tokenInfo); - continue; + secret_bytes_count = 0; } if(secret_bytes_count == 1) { // Plain secret key - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { - token_info_free(tokenInfo); - continue; + if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { + temp_cstr = furi_string_get_cstr(temp_str); + if(token_info_set_secret( + tokenInfo, temp_cstr, strlen(temp_cstr), &plugin_state->iv[0])) { + FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name); + result = TokenLoadingResultWarning; + } + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + result = TokenLoadingResultWarning; } - temp_cstr = furi_string_get_cstr(temp_str); - token_info_set_secret(tokenInfo, temp_cstr, strlen(temp_cstr), &plugin_state->iv[0]); has_any_plain_secret = true; - FURI_LOG_W(LOGGING_TAG, "Found token with plain secret"); } else { // encrypted tokenInfo->token_length = secret_bytes_count; - tokenInfo->token = malloc(tokenInfo->token_length); - if(!flipper_format_read_hex( - fff_data_file, - TOTP_CONFIG_KEY_TOKEN_SECRET, - tokenInfo->token, - tokenInfo->token_length)) { - token_info_free(tokenInfo); - continue; + if(secret_bytes_count > 0) { + tokenInfo->token = malloc(tokenInfo->token_length); + if(!flipper_format_read_hex( + fff_data_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + tokenInfo->token, + tokenInfo->token_length)) { + free(tokenInfo->token); + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + result = TokenLoadingResultWarning; + } + } else { + tokenInfo->token = NULL; + result = TokenLoadingResultWarning; } } - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) { - token_info_free(tokenInfo); - continue; + 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 { + tokenInfo->algo = SHA1; } - token_info_set_algo_from_str(tokenInfo, temp_str); - - if(!flipper_format_read_uint32( + if(flipper_format_read_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1)) { - token_info_free(tokenInfo); - continue; + token_info_set_digits_from_int(tokenInfo, temp_data32); + } else { + tokenInfo->digits = TOTP_6_DIGITS; } - token_info_set_digits_from_int(tokenInfo, temp_data32); - FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name); if(plugin_state->tokens_list == NULL) { @@ -407,6 +428,8 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { if(has_any_plain_secret) { totp_full_save_config_file(plugin_state); } + + return result; } void totp_close_config_file(FlipperFormat* file) { diff --git a/services/config/config.h b/services/config/config.h index 5e7a8c19d95..76cb40b2ccd 100644 --- a/services/config/config.h +++ b/services/config/config.h @@ -6,12 +6,18 @@ #include "../../types/token_info.h" #include "constants.h" +typedef enum { + TokenLoadingResultSuccess, + TokenLoadingResultWarning, + TokenLoadingResultError +} TokenLoadingResult; + Storage* totp_open_storage(); void totp_close_storage(); FlipperFormat* totp_open_config_file(Storage* storage); void totp_close_config_file(FlipperFormat* file); void totp_full_save_config_file(PluginState* const plugin_state); void totp_config_file_load_base(PluginState* const plugin_state); -void totp_config_file_load_tokens(PluginState* const plugin_state); +TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); void totp_config_file_save_new_token(TokenInfo* token_info); void totp_config_file_update_timezone_offset(float new_timezone_offset); diff --git a/types/token_info.c b/types/token_info.c index 8e1077ac68a..09ad1230aea 100644 --- a/types/token_info.c +++ b/types/token_info.c @@ -20,7 +20,7 @@ void token_info_free(TokenInfo* token_info) { free(token_info); } -void token_info_set_secret( +bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, uint8_t token_secret_length, @@ -28,12 +28,18 @@ void token_info_set_secret( uint8_t* plain_secret = malloc(token_secret_length); int plain_secret_length = base32_decode((uint8_t*)base32_token_secret, plain_secret, token_secret_length); - - token_info->token = - totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); + bool result; + if(plain_secret_length >= 0) { + token_info->token = + totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); + result = true; + } else { + result = false; + } memset(plain_secret, 0, token_secret_length); free(plain_secret); + return result; } uint8_t token_info_get_digits_count(TokenInfo* token_info) { diff --git a/types/token_info.h b/types/token_info.h index 2d531ec8931..e40c6e9bfad 100644 --- a/types/token_info.h +++ b/types/token_info.h @@ -16,7 +16,7 @@ typedef struct { TokenInfo* token_info_alloc(); void token_info_free(TokenInfo* token_info); -void token_info_set_secret( +bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, uint8_t token_secret_length,