MX
2023-04-26 23:50:37 +03:00
parent 6c0c0bd0be
commit 451ec9cba0
62 changed files with 2025 additions and 1679 deletions

View File

@@ -27,9 +27,6 @@ App(
Lib(
name="base64",
),
Lib(
name="linked_list"
),
Lib(
name="timezone_utils",
),

View File

@@ -63,7 +63,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) {
totp_cli_command_automation_handle(plugin_state, args, cli);
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) {
totp_cli_command_reset_handle(cli, cli_context->event_queue);
totp_cli_command_reset_handle(plugin_state, 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(

View File

@@ -3,6 +3,11 @@
#include <lib/toolbox/args.h>
#include "../types/plugin_event.h"
const char* TOTP_CLI_COLOR_ERROR = "91m";
const char* TOTP_CLI_COLOR_WARNING = "93m";
const char* TOTP_CLI_COLOR_SUCCESS = "92m";
const char* TOTP_CLI_COLOR_INFO = "96m";
bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) {
if(plugin_state->current_scene == TotpSceneAuthentication) {
TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n");
@@ -13,10 +18,11 @@ bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) {
furi_delay_ms(100);
}
TOTP_CLI_DELETE_LAST_LINE();
totp_cli_delete_last_line();
if(plugin_state->current_scene == TotpSceneAuthentication || //-V560
plugin_state->current_scene == TotpSceneNone) { //-V560
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
return false;
}
}
@@ -54,7 +60,7 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) {
} else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
size_t out_str_size = furi_string_size(out_str);
if(out_str_size > 0) {
TOTP_CLI_DELETE_LAST_CHAR();
totp_cli_delete_last_char();
furi_string_left(out_str, out_str_size - 1);
}
} else if(c == CliSymbolAsciiCR) {
@@ -83,3 +89,35 @@ void furi_string_secure_free(FuriString* str) {
furi_string_free(str);
}
void totp_cli_print_invalid_arguments() {
TOTP_CLI_PRINTF_ERROR(
"Invalid command arguments. use \"help\" command to get list of available commands");
}
void totp_cli_print_error_updating_config_file() {
TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n");
}
void totp_cli_print_error_loading_token_info() {
TOTP_CLI_PRINTF_ERROR("An error has occurred during loading token information\r\n");
}
void totp_cli_print_processing() {
TOTP_CLI_PRINTF("Processing, please wait...\r\n");
}
void totp_cli_delete_last_char() {
TOTP_CLI_PRINTF("\b \b");
fflush(stdout);
}
void totp_cli_delete_current_line() {
TOTP_CLI_PRINTF("\33[2K\r");
fflush(stdout);
}
void totp_cli_delete_last_line() {
TOTP_CLI_PRINTF("\033[A\33[2K\r");
fflush(stdout);
}

View File

@@ -14,6 +14,11 @@
#define DOCOPT_OPTIONS "[options]"
#define DOCOPT_DEFAULT(val) "[default: " val "]"
extern const char* TOTP_CLI_COLOR_ERROR;
extern const char* TOTP_CLI_COLOR_WARNING;
extern const char* TOTP_CLI_COLOR_SUCCESS;
extern const char* TOTP_CLI_COLOR_INFO;
#define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__)
#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
@@ -22,11 +27,6 @@
printf("\e[0m"); \
fflush(stdout)
#define TOTP_CLI_COLOR_ERROR "91m"
#define TOTP_CLI_COLOR_WARNING "93m"
#define TOTP_CLI_COLOR_SUCCESS "92m"
#define TOTP_CLI_COLOR_INFO "96m"
#define TOTP_CLI_PRINTF_ERROR(format, ...) \
TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__)
#define TOTP_CLI_PRINTF_WARNING(format, ...) \
@@ -36,24 +36,12 @@
#define TOTP_CLI_PRINTF_INFO(format, ...) \
TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__)
#define TOTP_CLI_DELETE_LAST_LINE() \
TOTP_CLI_PRINTF("\033[A\33[2K\r"); \
fflush(stdout)
#define TOTP_CLI_LOCK_UI(plugin_state) \
Scene __previous_scene = plugin_state->current_scene; \
totp_scene_director_activate_scene(plugin_state, TotpSceneStandby)
#define TOTP_CLI_DELETE_CURRENT_LINE() \
TOTP_CLI_PRINTF("\33[2K\r"); \
fflush(stdout)
#define TOTP_CLI_DELETE_LAST_CHAR() \
TOTP_CLI_PRINTF("\b \b"); \
fflush(stdout)
#define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \
TOTP_CLI_PRINTF_ERROR( \
"Invalid command arguments. use \"help\" command to get list of available commands")
#define TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE() \
TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n")
#define TOTP_CLI_UNLOCK_UI(plugin_state) \
totp_scene_director_activate_scene(plugin_state, __previous_scene)
/**
* @brief Checks whether user is authenticated and entered correct PIN.
@@ -92,3 +80,38 @@ bool args_read_uint8_and_trim(FuriString* args, uint8_t* value);
* @param str instance to free
*/
void furi_string_secure_free(FuriString* str);
/**
* @brief Deletes last printed line in console
*/
void totp_cli_delete_last_line();
/**
* @brief Deletes current printed line in console
*/
void totp_cli_delete_current_line();
/**
* @brief Deletes last printed char in console
*/
void totp_cli_delete_last_char();
/**
* @brief Prints error message about invalid command arguments
*/
void totp_cli_print_invalid_arguments();
/**
* @brief Prints error message about config file update error
*/
void totp_cli_print_error_updating_config_file();
/**
* @brief Prints error message about config file loading error
*/
void totp_cli_print_error_loading_token_info();
/**
* @brief Prints message to let user knwo that command is processing now
*/
void totp_cli_print_processing();

View File

@@ -1,7 +1,6 @@
#include "add.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
@@ -9,6 +8,77 @@
#include "../../../ui/scene_director.h"
#include "../../common_command_arguments.h"
struct TotpAddContext {
FuriString* args;
Cli* cli;
uint8_t* iv;
};
enum TotpIteratorUpdateTokenResultsEx {
TotpIteratorUpdateTokenResultInvalidSecret = 1,
TotpIteratorUpdateTokenResultCancelled = 2,
TotpIteratorUpdateTokenResultInvalidArguments = 3
};
static TotpIteratorUpdateTokenResult
add_token_handler(TokenInfo* token_info, const void* context) {
const struct TotpAddContext* context_t = context;
// Reading token name
if(!args_read_probably_quoted_string_and_trim(context_t->args, token_info->name)) {
return TotpIteratorUpdateTokenResultInvalidArguments;
}
FuriString* temp_str = furi_string_alloc();
// Read optional arguments
bool mask_user_input = true;
PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32;
while(args_read_string_and_trim(context_t->args, temp_str)) {
bool parsed = false;
if(!totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) &&
!totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_plain_token_secret_encoding(
temp_str, context_t->args, &parsed, &token_secret_encoding)) {
totp_cli_printf_unknown_argument(temp_str);
}
if(!parsed) {
furi_string_free(temp_str);
return TotpIteratorUpdateTokenResultInvalidArguments;
}
}
// 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(context_t->cli, temp_str, mask_user_input)) {
totp_cli_delete_last_line();
furi_string_secure_free(temp_str);
return TotpIteratorUpdateTokenResultCancelled;
}
totp_cli_delete_last_line();
bool secret_set = token_info_set_secret(
token_info,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
token_secret_encoding,
context_t->iv);
furi_string_secure_free(temp_str);
if(!secret_set) {
return TotpIteratorUpdateTokenResultInvalidSecret;
}
return TotpIteratorUpdateTokenResultSuccess;
}
void totp_cli_command_add_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT
", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n");
@@ -75,90 +145,33 @@ void totp_cli_command_add_docopt_options() {
}
void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
FuriString* temp_str = furi_string_alloc();
TokenInfo* token_info = token_info_alloc();
// Reading token name
if(!args_read_probably_quoted_string_and_trim(args, temp_str)) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
token_info_free(token_info);
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
size_t temp_cstr_len = furi_string_size(temp_str);
token_info->name = malloc(temp_cstr_len + 1);
furi_check(token_info->name != NULL);
strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1);
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
// Read optional arguments
bool mask_user_input = true;
PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
while(args_read_string_and_trim(args, temp_str)) {
bool parsed = false;
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_try_read_plain_token_secret_encoding(
temp_str, args, &parsed, &token_secret_encoding)) {
totp_cli_printf_unknown_argument(temp_str);
}
TOTP_CLI_LOCK_UI(plugin_state);
if(!parsed) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
token_info_free(token_info);
return;
}
}
struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]};
TotpIteratorUpdateTokenResult add_result =
totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context);
// 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();
if(add_result == TotpIteratorUpdateTokenResultSuccess) {
TOTP_CLI_PRINTF_SUCCESS(
"Token \"%s\" has been successfully added\r\n",
furi_string_get_cstr(
totp_token_info_iterator_get_current_token(iterator_context)->name));
} else if(add_result == TotpIteratorUpdateTokenResultCancelled) {
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();
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;
}
bool secret_set = token_info_set_secret(
token_info,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
token_secret_encoding,
plugin_state->iv);
furi_string_secure_free(temp_str);
if(secret_set) {
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check);
plugin_state->tokens_count++;
if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF_SUCCESS(
"Token \"%s\" has been successfully added\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
} else {
token_info_free(token_info);
} else if(add_result == TotpIteratorUpdateTokenResultInvalidArguments) {
totp_cli_print_invalid_arguments();
} else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
} else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
totp_cli_print_error_updating_config_file();
}
if(load_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
TOTP_CLI_UNLOCK_UI(plugin_state);
}

View File

@@ -31,7 +31,7 @@ void totp_cli_command_automation_docopt_arguments() {
"\r\n");
}
static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) {
static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) {
#ifdef TOTP_BADBT_TYPE_ENABLED
bool has_previous_method = false;
#endif
@@ -88,26 +88,20 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a
do {
if(!args_valid) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
totp_cli_print_invalid_arguments();
break;
}
if(new_method_provided) {
Scene previous_scene = TotpSceneNone;
if(plugin_state->current_scene == TotpSceneGenerateToken ||
plugin_state->current_scene == TotpSceneAppSettings) {
previous_scene = plugin_state->current_scene;
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
}
TOTP_CLI_LOCK_UI(plugin_state);
plugin_state->automation_method = new_method;
if(totp_config_file_update_automation_method(new_method) ==
TotpConfigFileUpdateSuccess) {
if(totp_config_file_update_automation_method(plugin_state)) {
TOTP_CLI_PRINTF_SUCCESS("Automation method is set to ");
totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS);
cli_nl();
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
totp_cli_print_error_updating_config_file();
}
#ifdef TOTP_BADBT_TYPE_ENABLED
@@ -118,9 +112,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a
}
#endif
if(previous_scene != TotpSceneNone) {
totp_scene_director_activate_scene(plugin_state, previous_scene, NULL);
}
TOTP_CLI_UNLOCK_UI(plugin_state);
} else {
TOTP_CLI_PRINTF_INFO("Current automation method is ");
totp_cli_command_automation_print_method(

View File

@@ -3,7 +3,6 @@
#include <stdlib.h>
#include <ctype.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
@@ -37,10 +36,13 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
return;
}
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
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();
(size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) {
totp_cli_print_invalid_arguments();
return;
}
@@ -51,23 +53,27 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
confirm_needed = false;
} else {
totp_cli_printf_unknown_argument(temp_str);
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
totp_cli_print_invalid_arguments();
furi_string_free(temp_str);
return;
}
}
furi_string_free(temp_str);
ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1);
TOTP_CLI_LOCK_UI(plugin_state);
TokenInfo* token_info = list_node->data;
size_t original_token_index =
totp_token_info_iterator_get_current_token_index(iterator_context);
totp_token_info_iterator_go_to(iterator_context, token_number - 1);
const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context);
const char* token_info_name = furi_string_get_cstr(token_info->name);
bool confirmed = !confirm_needed;
if(confirm_needed) {
TOTP_CLI_PRINTF_WARNING("WARNING!\r\n");
TOTP_CLI_PRINTF_WARNING(
"TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n",
token_info->name);
token_info_name);
TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n");
fflush(stdout);
char user_pick;
@@ -80,32 +86,21 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
}
if(confirmed) {
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
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;
}
plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node);
plugin_state->tokens_count--;
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
totp_cli_print_processing();
if(totp_token_info_iterator_remove_current_token_info(iterator_context)) {
totp_cli_delete_last_line();
TOTP_CLI_PRINTF_SUCCESS(
"Token \"%s\" has been successfully deleted\r\n", token_info->name);
"Token \"%s\" has been successfully deleted\r\n", token_info_name);
totp_token_info_iterator_go_to(iterator_context, 0);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
token_info_free(token_info);
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
totp_cli_delete_last_line();
totp_cli_print_error_updating_config_file();
totp_token_info_iterator_go_to(iterator_context, original_token_index);
}
} else {
TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n");
totp_token_info_iterator_go_to(iterator_context, original_token_index);
}
TOTP_CLI_UNLOCK_UI(plugin_state);
}

View File

@@ -1,9 +1,10 @@
#include "details.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/constants.h"
#include "../../../services/config/config.h"
#include "../../../ui/scene_director.h"
#include "../../cli_helpers.h"
#include "../../common_command_arguments.h"
@@ -17,21 +18,21 @@
} while(false)
static void print_automation_features(const TokenInfo* token_info) {
if(token_info->automation_features == TOKEN_AUTOMATION_FEATURE_NONE) {
if(token_info->automation_features == TokenAutomationFeatureNone) {
TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None");
return;
}
bool header_printed = false;
if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) {
if(token_info->automation_features & TokenAutomationFeatureEnterAtTheEnd) {
TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type <Enter> key at the end", header_printed);
}
if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) {
if(token_info->automation_features & TokenAutomationFeatureTabAtTheEnd) {
TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type <Tab> key at the end", header_printed);
}
if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
if(token_info->automation_features & TokenAutomationFeatureTypeSlower) {
TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type slower", header_printed);
}
}
@@ -53,26 +54,39 @@ void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args
}
int token_number;
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
(size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) {
totp_cli_print_invalid_arguments();
return;
}
ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1);
TOTP_CLI_LOCK_UI(plugin_state);
TokenInfo* token_info = list_node->data;
size_t original_token_index =
totp_token_info_iterator_get_current_token_index(iterator_context);
if(totp_token_info_iterator_go_to(iterator_context, token_number - 1)) {
const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context);
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");
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", furi_string_get_cstr(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");
} else {
totp_cli_print_error_loading_token_info();
}
totp_token_info_iterator_go_to(iterator_context, original_token_index);
TOTP_CLI_UNLOCK_UI(plugin_state);
}

View File

@@ -1,8 +1,9 @@
#include "list.h"
#include <stdlib.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/constants.h"
#include "../../../services/config/config.h"
#include "../../../ui/scene_director.h"
#include "../../cli_helpers.h"
void totp_cli_command_list_docopt_commands() {
@@ -20,25 +21,36 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) {
return;
}
if(plugin_state->tokens_list == NULL) {
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
size_t total_count = totp_token_info_iterator_get_total_count(iterator_context);
if(total_count <= 0) {
TOTP_CLI_PRINTF("There are no tokens");
return;
}
TOTP_CLI_LOCK_UI(plugin_state);
size_t original_index = totp_token_info_iterator_get_current_token_index(iterator_context);
TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n");
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, {
TokenInfo* token_info = (TokenInfo*)node->data;
for(size_t i = 0; i < total_count; i++) {
totp_token_info_iterator_go_to(iterator_context, i);
const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context);
TOTP_CLI_PRINTF(
"| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n",
index,
token_info->name,
i + 1,
furi_string_get_cstr(token_info->name),
token_info_get_algo_as_cstr(token_info),
token_info->digits,
token_info->duration);
index++;
});
}
TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n");
totp_token_info_iterator_go_to(iterator_context, original_index);
TOTP_CLI_UNLOCK_UI(plugin_state);
}

View File

@@ -2,7 +2,6 @@
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
@@ -33,42 +32,52 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C
return;
}
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();
int token_number;
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
size_t total_count = totp_token_info_iterator_get_total_count(iterator_context);
if(!args_read_int_and_trim(args, &token_number) || token_number < 1 ||
(size_t)token_number > total_count) {
totp_cli_print_invalid_arguments();
return;
}
int new_token_index = 0;
int new_token_number = 0;
if(!args_read_int_and_trim(args, &new_token_index) || new_token_index < 1 ||
new_token_index > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
if(!args_read_int_and_trim(args, &new_token_number) || new_token_number < 1 ||
(size_t)new_token_number > total_count) {
totp_cli_print_invalid_arguments();
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;
if(token_number == new_token_number) {
TOTP_CLI_PRINTF_ERROR("New token number matches current token number\r\n");
return;
}
TokenInfo* token_info = NULL;
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);
TOTP_CLI_LOCK_UI(plugin_state);
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
size_t token_index = token_number - 1;
size_t new_token_index = new_token_number - 1;
size_t original_token_index =
totp_token_info_iterator_get_current_token_index(iterator_context);
totp_cli_print_processing();
if(totp_token_info_iterator_go_to(iterator_context, token_index) &&
totp_token_info_iterator_move_current_token_info(iterator_context, new_token_index)) {
totp_cli_delete_last_line();
TOTP_CLI_PRINTF_SUCCESS(
"Token \"%s\" has been successfully updated\r\n", token_info->name);
"Token \"%s\" has been successfully updated\r\n",
furi_string_get_cstr(
totp_token_info_iterator_get_current_token(iterator_context)->name));
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
totp_cli_delete_last_line();
totp_cli_print_error_updating_config_file();
}
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
totp_token_info_iterator_go_to(iterator_context, original_token_index);
TOTP_CLI_UNLOCK_UI(plugin_state);
}

View File

@@ -28,7 +28,8 @@ void totp_cli_command_notification_docopt_arguments() {
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n");
}
static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) {
static void
totp_cli_command_notification_print_method(NotificationMethod method, const char* color) {
bool has_previous_method = false;
if(method & NotificationMethodSound) {
TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\"");
@@ -73,31 +74,23 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString*
do {
if(!args_valid) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
totp_cli_print_invalid_arguments();
break;
}
if(new_method_provided) {
Scene previous_scene = TotpSceneNone;
if(plugin_state->current_scene == TotpSceneGenerateToken ||
plugin_state->current_scene == TotpSceneAppSettings) {
previous_scene = plugin_state->current_scene;
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
}
TOTP_CLI_LOCK_UI(plugin_state);
plugin_state->notification_method = new_method;
if(totp_config_file_update_notification_method(new_method) ==
TotpConfigFileUpdateSuccess) {
if(totp_config_file_update_notification_method(plugin_state)) {
TOTP_CLI_PRINTF_SUCCESS("Notification method is set to ");
totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS);
cli_nl();
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
totp_cli_print_error_updating_config_file();
}
if(previous_scene != TotpSceneNone) {
totp_scene_director_activate_scene(plugin_state, previous_scene, NULL);
}
TOTP_CLI_UNLOCK_UI(plugin_state);
} else {
TOTP_CLI_PRINTF_INFO("Current notification method is ");
totp_cli_command_notification_print_method(

View File

@@ -2,7 +2,6 @@
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../types/user_pin_codes.h"
#include "../../../services/config/config.h"
@@ -65,14 +64,14 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) {
}
}
} else if(c == CliSymbolAsciiETX) {
TOTP_CLI_DELETE_CURRENT_LINE();
totp_cli_delete_current_line();
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
return false;
} else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
if(*pin_length > 0) {
*pin_length = *pin_length - 1;
pin[*pin_length] = 0;
TOTP_CLI_DELETE_LAST_CHAR();
totp_cli_delete_last_char();
}
} else if(c == CliSymbolAsciiCR) {
cli_nl();
@@ -80,7 +79,7 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) {
}
}
TOTP_CLI_DELETE_LAST_LINE();
totp_cli_delete_last_line();
return true;
}
@@ -97,22 +96,22 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) {
do_remove = true;
} else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
totp_cli_print_invalid_arguments();
}
} else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
totp_cli_print_invalid_arguments();
}
if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) {
bool load_generate_token_scene = false;
TOTP_CLI_LOCK_UI(plugin_state);
do {
uint8_t old_iv[TOTP_IV_SIZE];
memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE);
uint8_t new_pin[TOTP_IV_SIZE];
memset(&new_pin[0], 0, TOTP_IV_SIZE);
uint8_t new_pin_length = 0;
if(do_change) {
if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length) ||
!totp_cli_ensure_authenticated(plugin_state, cli)) {
if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) {
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
break;
}
@@ -121,7 +120,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
memset(&new_pin[0], 0, TOTP_IV_SIZE);
}
char* backup_path = totp_config_file_backup();
char* backup_path = totp_config_file_backup(plugin_state);
if(backup_path != NULL) {
TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path);
TOTP_CLI_PRINTF_WARNING(
@@ -134,61 +133,28 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
break;
}
if(plugin_state->current_scene == TotpSceneGenerateToken) {
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
load_generate_token_scene = true;
}
TOTP_CLI_PRINTF("Encrypting...\r\n");
TOTP_CLI_PRINTF("Encrypting, please wait...\r\n");
memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE);
memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE);
if(plugin_state->crypto_verify_data != NULL) {
free(plugin_state->crypto_verify_data);
plugin_state->crypto_verify_data = NULL;
}
if(!totp_crypto_seed_iv(
plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length)) {
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
break;
}
bool update_result =
totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length);
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
TokenInfo* token_info = node->data;
size_t plain_token_length;
uint8_t* plain_token = totp_crypto_decrypt(
token_info->token, token_info->token_length, &old_iv[0], &plain_token_length);
free(token_info->token);
token_info->token = totp_crypto_encrypt(
plain_token,
plain_token_length,
&plugin_state->iv[0],
&token_info->token_length);
memset_s(plain_token, plain_token_length, 0, plain_token_length);
free(plain_token);
});
totp_cli_delete_last_line();
TOTP_CLI_DELETE_LAST_LINE();
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
if(update_result) {
if(do_change) {
TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n");
} else if(do_remove) {
TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n");
}
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
totp_cli_print_error_updating_config_file();
}
} while(false);
if(load_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
TOTP_CLI_UNLOCK_UI(plugin_state);
}
furi_string_free(temp_str);

View File

@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <furi/core/string.h>
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
#include "../../../services/config/config.h"
#define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES"
@@ -16,7 +17,11 @@ void totp_cli_command_reset_docopt_usage() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n");
}
void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) {
void totp_cli_command_reset_handle(
PluginState* plugin_state,
Cli* cli,
FuriMessageQueue* event_queue) {
TOTP_CLI_LOCK_UI(plugin_state);
TOTP_CLI_PRINTF_WARNING(
"As a result of reset all the settings and tokens will be permanently lost.\r\n");
TOTP_CLI_PRINTF_WARNING("Do you really want to reset application?\r\n");
@@ -27,11 +32,12 @@ void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) {
furi_string_cmpi_str(temp_str, TOTP_CLI_RESET_CONFIRMATION_KEYWORD) == 0;
furi_string_free(temp_str);
if(is_confirmed) {
totp_config_file_reset();
totp_config_file_reset(plugin_state);
TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n");
TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n");
totp_cli_force_close_app(event_queue);
} else {
TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n");
TOTP_CLI_UNLOCK_UI(plugin_state);
}
}

View File

@@ -5,6 +5,9 @@
#define TOTP_CLI_COMMAND_RESET "reset"
void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue);
void totp_cli_command_reset_handle(
PluginState* plugin_state,
Cli* cli,
FuriMessageQueue* event_queue);
void totp_cli_command_reset_docopt_commands();
void totp_cli_command_reset_docopt_usage();

View File

@@ -33,19 +33,14 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
char* strtof_endptr;
float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr);
if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
TOTP_CLI_LOCK_UI(plugin_state);
plugin_state->timezone_offset = tz;
if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) {
if(totp_config_file_update_timezone_offset(plugin_state)) {
TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
if(plugin_state->current_scene == TotpSceneGenerateToken) {
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} else if(plugin_state->current_scene == TotpSceneAppSettings) {
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL);
totp_cli_print_error_updating_config_file();
}
TOTP_CLI_UNLOCK_UI(plugin_state);
} else {
TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
}

View File

@@ -1,7 +1,6 @@
#include "update.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
@@ -11,6 +10,103 @@
#define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s"
struct TotpUpdateContext {
FuriString* args;
Cli* cli;
uint8_t* iv;
};
enum TotpIteratorUpdateTokenResultsEx {
TotpIteratorUpdateTokenResultInvalidSecret = 1,
TotpIteratorUpdateTokenResultCancelled = 2,
TotpIteratorUpdateTokenResultInvalidArguments = 3
};
static bool totp_cli_try_read_name(
TokenInfo* token_info,
const 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, token_info->name) ||
furi_string_empty(token_info->name)) {
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX);
} else {
*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;
}
static TotpIteratorUpdateTokenResult
update_token_handler(TokenInfo* token_info, const void* context) {
const struct TotpUpdateContext* context_t = context;
// Read optional arguments
FuriString* temp_str = furi_string_alloc();
bool mask_user_input = true;
bool update_token_secret = false;
PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32;
while(args_read_string_and_trim(context_t->args, temp_str)) {
bool parsed = false;
if(!totp_cli_try_read_name(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) &&
!totp_cli_try_read_duration(token_info, temp_str, context_t->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, context_t->args, &parsed) &&
!totp_cli_try_read_plain_token_secret_encoding(
temp_str, context_t->args, &parsed, &token_secret_encoding)) {
totp_cli_printf_unknown_argument(temp_str);
}
if(!parsed) {
furi_string_free(temp_str);
return TotpIteratorUpdateTokenResultInvalidArguments;
}
}
if(update_token_secret) {
// Reading token secret
furi_string_reset(temp_str);
TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n");
bool token_secret_read = totp_cli_read_line(context_t->cli, temp_str, mask_user_input);
totp_cli_delete_last_line();
if(!token_secret_read) {
furi_string_secure_free(temp_str);
return TotpIteratorUpdateTokenResultCancelled;
}
if(!token_info_set_secret(
token_info,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
token_secret_encoding,
context_t->iv)) {
furi_string_secure_free(temp_str);
return TotpIteratorUpdateTokenResultInvalidSecret;
}
}
furi_string_secure_free(temp_str);
return TotpIteratorUpdateTokenResultSuccess;
}
void totp_cli_command_update_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n");
}
@@ -34,136 +130,46 @@ void totp_cli_command_update_docopt_options() {
TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\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;
}
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
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();
(size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) {
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);
TOTP_CLI_LOCK_UI(plugin_state);
// Read optional arguments
bool mask_user_input = true;
bool update_token_secret = false;
PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
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_try_read_plain_token_secret_encoding(
temp_str, args, &parsed, &token_secret_encoding)) {
totp_cli_printf_unknown_argument(temp_str);
}
size_t previous_index = totp_token_info_iterator_get_current_token_index(iterator_context);
totp_token_info_iterator_go_to(iterator_context, token_number - 1);
if(!parsed) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
token_info_free(token_info);
return;
}
struct TotpUpdateContext update_context = {
.args = args, .cli = cli, .iv = &plugin_state->iv[0]};
TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token(
iterator_context, &update_token_handler, &update_context);
if(update_result == TotpIteratorUpdateTokenResultSuccess) {
TOTP_CLI_PRINTF_SUCCESS(
"Token \"%s\" has been successfully updated\r\n",
furi_string_get_cstr(
totp_token_info_iterator_get_current_token(iterator_context)->name));
} else if(update_result == TotpIteratorUpdateTokenResultInvalidArguments) {
totp_cli_print_invalid_arguments();
} else if(update_result == TotpIteratorUpdateTokenResultCancelled) {
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
} else if(update_result == TotpIteratorUpdateTokenResultInvalidSecret) {
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
} else if(update_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
totp_cli_print_error_updating_config_file();
}
bool token_secret_read = false;
if(update_token_secret) {
// Reading token secret
furi_string_reset(temp_str);
TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n");
token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input);
TOTP_CLI_DELETE_LAST_LINE();
if(!token_secret_read) {
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
}
}
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;
}
bool token_secret_set = false;
if(update_token_secret && token_secret_read) {
if(token_info->token != NULL) {
free(token_info->token);
}
token_secret_set = token_info_set_secret(
token_info,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
token_secret_encoding,
plugin_state->iv);
if(!token_secret_set) {
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
}
}
furi_string_secure_free(temp_str);
if(!update_token_secret || (token_secret_read && token_secret_set)) {
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);
}
} else {
token_info_free(token_info);
}
if(load_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
totp_token_info_iterator_go_to(iterator_context, previous_index);
TOTP_CLI_UNLOCK_UI(plugin_state);
}

View File

@@ -1,11 +1,11 @@
#include "common_command_arguments.h"
#include <lib/toolbox/args.h>
inline void totp_cli_printf_missed_argument_value(char* arg) {
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) {
void totp_cli_printf_unknown_argument(const FuriString* arg) {
TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(arg));
}
@@ -121,10 +121,10 @@ bool totp_cli_try_read_plain_token_secret_encoding(
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX);
} else {
if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) {
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
*secret_encoding = PlainTokenSecretEncodingBase32;
*parsed = true;
} else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) {
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE64;
*secret_encoding = PlainTokenSecretEncodingBase64;
*parsed = true;
} else {
TOTP_CLI_PRINTF_ERROR(

View File

@@ -19,23 +19,29 @@
#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding"
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);
bool totp_cli_try_read_plain_token_secret_encoding(

View File

@@ -1,6 +1,7 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
* Modified and optimized for Flipepr Zero device purposes by Alex Kopachov (@akopachov)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View File

@@ -1,136 +0,0 @@
#include "linked_list.h"
ListNode* list_init_head(void* data) {
ListNode* new = malloc(sizeof(ListNode));
if(new == NULL) return NULL;
new->data = data;
new->next = NULL;
return new;
}
ListNode* list_add(ListNode* head, void* data) {
ListNode* new = malloc(sizeof(ListNode));
if(new == NULL) return NULL;
new->data = data;
new->next = NULL;
if(head == NULL)
head = new;
else {
ListNode* it;
for(it = head; it->next != NULL; it = it->next)
;
it->next = new;
}
return head;
}
ListNode* list_find(ListNode* head, const void* data) {
ListNode* it = NULL;
for(it = head; it != NULL; it = it->next)
if(it->data == data) break;
return it;
}
ListNode* list_element_at(ListNode* head, uint16_t index) {
ListNode* it;
uint16_t i;
for(it = head, i = 0; it != NULL && i < index; it = it->next, i++)
;
return it;
}
ListNode* list_remove(ListNode* head, ListNode* ep) {
if(head == NULL) {
return NULL;
}
if(head == ep) {
ListNode* new_head = head->next;
free(head);
return new_head;
}
ListNode* it;
for(it = head; it->next != ep; it = it->next)
;
it->next = ep->next;
free(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;
while(it != NULL) {
tmp = it;
it = it->next;
free(tmp);
}
}

View File

@@ -1,98 +0,0 @@
#pragma once
#include <stdlib.h>
#include <inttypes.h>
/**
* @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. */
/**
* @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) \
if(head == NULL) { \
head = list_init_head(item); \
assert(head != NULL); \
} else { \
assert(list_add(head, item) != NULL); \
}
#define TOTP_LIST_FOREACH(head, node, action) \
ListNode* node = head; \
while(node != NULL) { \
action node = node->next; \
}

View File

@@ -25,4 +25,4 @@ TOTP_ROLL_VALUE_FN(int8_t, int8_t)
TOTP_ROLL_VALUE_FN(uint8_t, int8_t)
TOTP_ROLL_VALUE_FN(uint16_t, int16_t);
TOTP_ROLL_VALUE_FN(size_t, int16_t);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
typedef uint8_t TotpRollValueOverflowBehavior;
@@ -47,7 +48,7 @@ TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t);
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.
* @brief Rolls \c size_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
@@ -55,4 +56,4 @@ TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t);
* @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);
TOTP_ROLL_VALUE_FN_HEADER(size_t, int16_t);

File diff suppressed because it is too large Load Diff

View File

@@ -1,137 +1,90 @@
#pragma once
#include <flipper_format/flipper_format.h>
#include "../../types/plugin_state.h"
#include "../../types/token_info.h"
#include "config_file_context.h"
#include "constants.h"
#include "token_info_iterator.h"
typedef uint8_t TokenLoadingResult;
typedef uint8_t TotpConfigFileOpenResult;
typedef uint8_t TotpConfigFileUpdateResult;
/**
* @brief Token loading results
*/
enum TokenLoadingResults {
/**
* @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
};
/**
* @brief Config file opening result
*/
enum TotpConfigFileOpenResults {
/**
* @brief Config file opened successfully
*/
TotpConfigFileOpenSuccess = 0,
/**
* @brief An error has occurred during opening config file
*/
TotpConfigFileOpenError = 1
};
/**
* @brief Config file updating result
*/
enum TotpConfigFileUpdateResults {
/**
* @brief Config file updated successfully
*/
TotpConfigFileUpdateSuccess,
/**
* @brief An error has occurred during updating config file
*/
TotpConfigFileUpdateError
};
/**
* @brief Tries to take a config file backup
* @param plugin_state application state
* @return backup path if backup successfully taken; \c NULL otherwise
*/
char* totp_config_file_backup();
/**
* @brief Saves all the settings and tokens to an application config file
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state);
char* totp_config_file_backup(const PluginState* plugin_state);
/**
* @brief Loads basic information from an application config file into application state without loading all the tokens
* @param plugin_state application state
* @return Config file open result
*/
TotpConfigFileOpenResult 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
* @return Config file update result
*/
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info);
bool totp_config_file_load(PluginState* const plugin_state);
/**
* @brief Updates timezone offset in an application config file
* @param new_timezone_offset new timezone offset to be set
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset);
bool totp_config_file_update_timezone_offset(const PluginState* plugin_state);
/**
* @brief Updates notification method in an application config file
* @param new_notification_method new notification method to be set
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult
totp_config_file_update_notification_method(NotificationMethod new_notification_method);
bool totp_config_file_update_notification_method(const PluginState* plugin_state);
/**
* @brief Updates automation method in an application config file
* @param new_automation_method new automation method to be set
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult
totp_config_file_update_automation_method(AutomationMethod new_automation_method);
bool totp_config_file_update_automation_method(const PluginState* plugin_state);
/**
* @brief Updates application user settings
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state);
bool totp_config_file_update_user_settings(const PluginState* plugin_state);
/**
* @brief Updates crypto signatures information
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult
totp_config_file_update_crypto_signatures(const PluginState* plugin_state);
bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state);
/**
* @brief Reset all the settings to default
* @param plugin_state application state
*/
void totp_config_file_reset();
void totp_config_file_reset(PluginState* const plugin_state);
/**
* @brief Closes config file and releases all the resources
* @param plugin_state application state
*/
void totp_config_file_close(PluginState* const plugin_state);
/**
* @brief Updates config file encryption by re-encrypting it using new user's PIN and new randomly generated IV
* @param plugin_state application state
* @param new_pin new user's PIN
* @param new_pin_length new user's PIN length
* @return \c true if config file encryption successfully updated; \c false otherwise
*/
bool totp_config_file_update_encryption(
PluginState* plugin_state,
const uint8_t* new_pin,
uint8_t new_pin_length);
/**
* @brief Gets token info iterator context
* @param plugin_state application state
* @return token info iterator context
*/
TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state);

View File

@@ -0,0 +1,3 @@
#pragma once
typedef struct ConfigFileContext ConfigFileContext;

View File

@@ -1,7 +1,10 @@
#pragma once
#include <storage/storage.h>
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator")
#define CONFIG_FILE_HEADER "Flipper TOTP plugin config file"
#define CONFIG_FILE_ACTUAL_VERSION (4)
#define CONFIG_FILE_ACTUAL_VERSION (5)
#define TOTP_CONFIG_KEY_TIMEZONE "Timezone"
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"

View File

@@ -1,6 +1,7 @@
#include "common_migration.h"
#include "../constants.h"
#include "../../../types/token_info.h"
#include <flipper_format/flipper_format_i.h>
bool totp_config_migrate_to_latest(
FlipperFormat* fff_data_file,
@@ -57,18 +58,12 @@ bool totp_config_migrate_to_latest(
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(
@@ -78,15 +73,32 @@ bool totp_config_migrate_to_latest(
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);
if(current_version < 5) {
uint32_t algo_as_uint32t = SHA1;
if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA256_NAME) == 0) {
algo_as_uint32t = SHA256;
} else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA512_NAME) == 0) {
algo_as_uint32t = SHA512;
} else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) {
algo_as_uint32t = STEAM;
}
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &algo_as_uint32t, 1);
} else {
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;
const uint32_t default_algo = SHA1;
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &default_algo, 1);
const uint32_t default_digits = TotpSixDigitsCount;
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1);
}
@@ -108,18 +120,21 @@ bool totp_config_migrate_to_latest(
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;
const uint32_t default_automation_features = TokenAutomationFeatureNone;
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);
Stream* stream = flipper_format_get_raw_stream(fff_data_file);
size_t current_pos = stream_tell(stream);
size_t total_size = stream_size(stream);
if(current_pos < total_size) {
stream_delete(stream, total_size - current_pos);
}
result = true;
} while(false);

View File

@@ -2,6 +2,12 @@
#include <flipper_format/flipper_format.h>
/**
* @brief Migrates config file to the latest version
* @param fff_data_file original config file to be migrated
* @param fff_backup_data_file backup copy of original config file
* @return \c true if operation succeeded; \c false otherwise
*/
bool totp_config_migrate_to_latest(
FlipperFormat* fff_data_file,
FlipperFormat* fff_backup_data_file);
FlipperFormat* fff_backup_data_file);

View File

@@ -0,0 +1,551 @@
#include "token_info_iterator.h"
#include <flipper_format/flipper_format_i.h>
#include <flipper_format/flipper_format_stream.h>
#include <toolbox/stream/file_stream.h>
#include "../../types/common.h"
#define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part"
#define STREAM_COPY_BUFFER_SIZE 128
struct TokenInfoIteratorContext {
size_t total_count;
size_t current_index;
size_t last_seek_offset;
size_t last_seek_index;
TokenInfo* current_token;
FlipperFormat* config_file;
uint8_t* iv;
Storage* storage;
};
static bool
flipper_format_seek_to_siblinig_token_start(Stream* stream, StreamDirection direction) {
char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_NAME) + 1];
bool found = false;
while(!found) {
if(!stream_seek_to_char(stream, '\n', direction)) {
break;
}
size_t buffer_read_size;
if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) {
break;
}
if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) {
break;
}
if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_NAME ":", sizeof(buffer)) == 0) {
found = true;
}
}
return found;
}
static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) {
furi_check(context != NULL && context->config_file != NULL);
if(token_index >= context->total_count) {
return false;
}
Stream* stream = flipper_format_get_raw_stream(context->config_file);
long token_index_diff = (long)token_index - (long)context->last_seek_index;
size_t token_index_diff_weight = (size_t)labs(token_index_diff);
StreamDirection direction = token_index_diff >= 0 ? StreamDirectionForward :
StreamDirectionBackward;
if(token_index_diff_weight > token_index || context->last_seek_offset == 0) {
context->last_seek_offset = 0;
context->last_seek_index = 0;
token_index_diff = token_index + 1;
direction = StreamDirectionForward;
} else if(token_index_diff_weight > (context->total_count - token_index - 1)) {
context->last_seek_offset = stream_size(stream);
context->last_seek_index = context->total_count - 1;
token_index_diff = -(long)(context->total_count - token_index);
direction = StreamDirectionBackward;
}
stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart);
if(token_index_diff != 0) {
long i = 0;
long i_inc = token_index_diff >= 0 ? 1 : -1;
do {
if(!flipper_format_seek_to_siblinig_token_start(stream, direction)) {
break;
}
i += i_inc;
} while((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff));
if((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)) {
context->last_seek_offset = 0;
FURI_LOG_D(LOGGING_TAG, "Was not able to move");
return false;
}
context->last_seek_offset = stream_tell(stream);
context->last_seek_index = token_index;
} else {
if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) {
return false;
}
}
return true;
}
static bool stream_insert_stream(Stream* dst, Stream* src) {
uint8_t buffer[STREAM_COPY_BUFFER_SIZE];
size_t buffer_read_size;
while((buffer_read_size = stream_read(src, buffer, sizeof(buffer))) != 0) {
if(!stream_insert(dst, buffer, buffer_read_size)) {
return false;
}
}
return true;
}
static bool ensure_stream_ends_with_lf(Stream* stream) {
uint8_t last_char;
size_t original_pos = stream_tell(stream);
if(!stream_seek(stream, -1, StreamOffsetFromEnd) || stream_read(stream, &last_char, 1) < 1) {
return false;
}
const uint8_t lf = '\n';
if(last_char != lf && !stream_write(stream, &lf, 1)) {
return false;
}
if(!stream_seek(stream, original_pos, StreamOffsetFromStart)) {
return false;
}
return true;
}
static bool
totp_token_info_iterator_save_current_token_info_changes(TokenInfoIteratorContext* context) {
bool is_new_token = context->current_index >= context->total_count;
Stream* stream = flipper_format_get_raw_stream(context->config_file);
if(is_new_token) {
if(!ensure_stream_ends_with_lf(stream) ||
!flipper_format_seek_to_end(context->config_file)) {
return false;
}
} else {
if(!seek_to_token(context->current_index, context)) {
return false;
}
}
size_t offset_start = stream_tell(stream);
size_t offset_end;
if(is_new_token) {
offset_end = offset_start;
} else if(context->current_index + 1 >= context->total_count) {
offset_end = stream_size(stream);
} else if(seek_to_token(context->current_index + 1, context)) {
offset_end = stream_tell(stream);
} else {
return false;
}
FlipperFormat* temp_ff = flipper_format_file_alloc(context->storage);
if(!flipper_format_file_open_always(temp_ff, CONFIG_FILE_PART_FILE_PATH)) {
flipper_format_free(temp_ff);
return false;
}
TokenInfo* token_info = context->current_token;
bool result = false;
do {
if(!flipper_format_write_string(temp_ff, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) {
break;
}
if(!flipper_format_write_hex(
temp_ff,
TOTP_CONFIG_KEY_TOKEN_SECRET,
token_info->token,
token_info->token_length)) {
break;
}
uint32_t tmp_uint32 = token_info->algo;
if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_ALGO, &tmp_uint32, 1)) {
break;
}
tmp_uint32 = token_info->digits;
if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) {
break;
}
tmp_uint32 = token_info->duration;
if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) {
break;
}
tmp_uint32 = token_info->automation_features;
if(!flipper_format_write_uint32(
temp_ff, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) {
break;
}
Stream* temp_stream = flipper_format_get_raw_stream(temp_ff);
if(!stream_rewind(temp_stream)) {
break;
}
if(!stream_seek(stream, offset_start, StreamOffsetFromStart)) {
break;
}
if(offset_end != offset_start && !stream_delete(stream, offset_end - offset_start)) {
break;
}
if(!is_new_token && !stream_write_char(stream, '\n')) {
break;
}
if(!stream_insert_stream(stream, temp_stream)) {
break;
}
if(is_new_token) {
context->total_count++;
}
result = true;
} while(false);
flipper_format_free(temp_ff);
storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH);
stream_seek(stream, offset_start, StreamOffsetFromStart);
context->last_seek_offset = offset_start;
context->last_seek_index = context->current_index;
return result;
}
TokenInfoIteratorContext*
totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv) {
Stream* stream = flipper_format_get_raw_stream(config_file);
stream_rewind(stream);
size_t tokens_count = 0;
while(true) {
if(!flipper_format_seek_to_siblinig_token_start(stream, StreamDirectionForward)) {
break;
}
tokens_count++;
}
TokenInfoIteratorContext* context = malloc(sizeof(TokenInfoIteratorContext));
furi_check(context != NULL);
context->total_count = tokens_count;
context->current_token = token_info_alloc();
context->config_file = config_file;
context->iv = iv;
context->storage = storage;
return context;
}
void totp_token_info_iterator_free(TokenInfoIteratorContext* context) {
if(context == NULL) return;
token_info_free(context->current_token);
free(context);
}
bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context) {
if(!seek_to_token(context->current_index, context)) {
return false;
}
Stream* stream = flipper_format_get_raw_stream(context->config_file);
size_t begin_offset = stream_tell(stream);
size_t end_offset;
if(!ensure_stream_ends_with_lf(stream)) {
return false;
}
if(context->current_index >= context->total_count - 1) {
end_offset = stream_size(stream) - 1;
} else if(seek_to_token(context->current_index + 1, context)) {
end_offset = stream_tell(stream);
} else {
return false;
}
if(!stream_seek(stream, begin_offset, StreamOffsetFromStart) ||
!stream_delete(stream, end_offset - begin_offset)) {
return false;
}
context->total_count--;
if(context->current_index >= context->total_count) {
context->current_index = context->total_count - 1;
}
return true;
}
bool totp_token_info_iterator_move_current_token_info(
TokenInfoIteratorContext* context,
size_t new_index) {
if(context->current_index == new_index) return true;
Stream* stream = flipper_format_get_raw_stream(context->config_file);
if(!ensure_stream_ends_with_lf(stream)) {
return false;
}
if(!seek_to_token(context->current_index, context)) {
return false;
}
size_t begin_offset = stream_tell(stream);
size_t end_offset;
if(context->current_index >= context->total_count - 1) {
end_offset = stream_size(stream) - 1;
} else if(seek_to_token(context->current_index + 1, context)) {
end_offset = stream_tell(stream);
} else {
return false;
}
Stream* temp_stream = file_stream_alloc(context->storage);
if(!file_stream_open(
temp_stream, CONFIG_FILE_PART_FILE_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) {
stream_free(temp_stream);
return false;
}
size_t moving_size = end_offset - begin_offset;
bool result = false;
do {
if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) {
break;
}
if(stream_copy(stream, temp_stream, moving_size) < moving_size) {
break;
}
if(!stream_rewind(temp_stream)) {
break;
}
if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) {
break;
}
if(!stream_delete(stream, moving_size)) {
break;
}
context->last_seek_offset = 0;
context->last_seek_index = 0;
if(new_index >= context->total_count - 1) {
if(!stream_seek(stream, stream_size(stream) - 1, StreamOffsetFromStart)) {
break;
}
} else if(!seek_to_token(new_index, context)) {
break;
}
result = stream_insert_stream(stream, temp_stream);
} while(false);
stream_free(temp_stream);
storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH);
context->last_seek_offset = 0;
context->last_seek_index = 0;
return result;
}
TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token(
TokenInfoIteratorContext* context,
TOTP_ITERATOR_UPDATE_TOKEN_ACTION update,
const void* update_context) {
TotpIteratorUpdateTokenResult result = update(context->current_token, update_context);
if(result == TotpIteratorUpdateTokenResultSuccess) {
if(!totp_token_info_iterator_save_current_token_info_changes(context)) {
result = TotpIteratorUpdateTokenResultFileUpdateFailed;
}
return result;
}
totp_token_info_iterator_go_to(context, context->current_index);
return result;
}
TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token(
TokenInfoIteratorContext* context,
TOTP_ITERATOR_UPDATE_TOKEN_ACTION update,
const void* update_context) {
size_t previous_index = context->current_index;
context->current_index = context->total_count;
token_info_set_defaults(context->current_token);
TotpIteratorUpdateTokenResult result = update(context->current_token, update_context);
if(result == TotpIteratorUpdateTokenResultSuccess &&
!totp_token_info_iterator_save_current_token_info_changes(context)) {
result = TotpIteratorUpdateTokenResultFileUpdateFailed;
}
if(result != TotpIteratorUpdateTokenResultSuccess) {
totp_token_info_iterator_go_to(context, previous_index);
}
return result;
}
bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index) {
furi_check(context != NULL);
context->current_index = token_index;
if(!seek_to_token(context->current_index, context)) {
return false;
}
Stream* stream = flipper_format_get_raw_stream(context->config_file);
size_t original_offset = stream_tell(stream);
if(!flipper_format_read_string(
context->config_file, TOTP_CONFIG_KEY_TOKEN_NAME, context->current_token->name)) {
stream_seek(stream, original_offset, StreamOffsetFromStart);
return false;
}
uint32_t secret_bytes_count;
if(!flipper_format_get_value_count(
context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) {
secret_bytes_count = 0;
}
TokenInfo* tokenInfo = context->current_token;
bool token_update_needed = false;
if(tokenInfo->token != NULL) {
free(tokenInfo->token);
tokenInfo->token_length = 0;
}
if(secret_bytes_count == 1) { // Plain secret key
FuriString* temp_str = furi_string_alloc();
if(flipper_format_read_string(
context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) {
if(token_info_set_secret(
tokenInfo,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
PlainTokenSecretEncodingBase32,
context->iv)) {
FURI_LOG_W(
LOGGING_TAG,
"Token \"%s\" has plain secret",
furi_string_get_cstr(tokenInfo->name));
token_update_needed = true;
} else {
tokenInfo->token = NULL;
tokenInfo->token_length = 0;
FURI_LOG_W(
LOGGING_TAG,
"Token \"%s\" has invalid secret",
furi_string_get_cstr(tokenInfo->name));
}
} else {
tokenInfo->token = NULL;
tokenInfo->token_length = 0;
}
furi_string_free(temp_str);
} else { // encrypted
tokenInfo->token_length = secret_bytes_count;
if(secret_bytes_count > 0) {
tokenInfo->token = malloc(tokenInfo->token_length);
furi_check(tokenInfo->token != NULL);
if(!flipper_format_read_hex(
context->config_file,
TOTP_CONFIG_KEY_TOKEN_SECRET,
tokenInfo->token,
tokenInfo->token_length)) {
free(tokenInfo->token);
tokenInfo->token = NULL;
tokenInfo->token_length = 0;
}
} else {
tokenInfo->token = NULL;
}
}
uint32_t temp_data32;
if(flipper_format_read_uint32(
context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) &&
temp_data32 <= STEAM) {
tokenInfo->algo = (TokenHashAlgo)temp_data32;
} else {
tokenInfo->algo = SHA1;
}
if(!flipper_format_read_uint32(
context->config_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) ||
!token_info_set_digits_from_int(tokenInfo, temp_data32)) {
tokenInfo->digits = TotpSixDigitsCount;
}
if(!flipper_format_read_uint32(
context->config_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) ||
!token_info_set_duration_from_int(tokenInfo, temp_data32)) {
tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT;
}
if(flipper_format_read_uint32(
context->config_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) {
tokenInfo->automation_features = temp_data32;
} else {
tokenInfo->automation_features = TokenAutomationFeatureNone;
}
stream_seek(stream, original_offset, StreamOffsetFromStart);
if(token_update_needed && !totp_token_info_iterator_save_current_token_info_changes(context)) {
return false;
}
return true;
}
const TokenInfo*
totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context) {
return context->current_token;
}
size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context) {
return context->current_index;
}
size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context) {
return context->total_count;
}
void totp_token_info_iterator_attach_to_config_file(
TokenInfoIteratorContext* context,
FlipperFormat* config_file) {
context->config_file = config_file;
}

View File

@@ -0,0 +1,121 @@
#pragma once
#include "../../types/token_info.h"
#include <flipper_format/flipper_format.h>
#include "constants.h"
typedef int TotpIteratorUpdateTokenResult;
typedef TotpIteratorUpdateTokenResult (
*TOTP_ITERATOR_UPDATE_TOKEN_ACTION)(TokenInfo* const token_info, const void* context);
typedef struct TokenInfoIteratorContext TokenInfoIteratorContext;
enum TotpIteratorUpdateTokenResults {
/**
* @brief Token successfully updated
*/
TotpIteratorUpdateTokenResultSuccess = 0,
/**
* @brief An error ocurred during updating config file
*/
TotpIteratorUpdateTokenResultFileUpdateFailed = -1
};
/**
* @brief Initializes a new token info iterator
* @param storage storage reference
* @param config_file config file to use
* @param iv initialization vector (IV) to be used for encryption\decryption
* @return Token info iterator context
*/
TokenInfoIteratorContext*
totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv);
/**
* @brief Navigates iterator to the token with given index
* @param context token info iterator context
* @param token_index token index to navigate to
* @return \c true if navigation succeeded; \c false otherwise
*/
bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index);
/**
* @brief Moves current token to a given new index
* @param context token info iterator context
* @param new_index new token index to move current token to
* @return \c true if operation succeeded; \c false otherwise
*/
bool totp_token_info_iterator_move_current_token_info(
TokenInfoIteratorContext* context,
size_t new_index);
/**
* @brief Updates current token info using given update action
* @param context token info iterator context
* @param update action which is responsible to make all the necessary updates to token info
* @param update_context update action context
* @return \c true if operation succeeded; \c false otherwise
*/
TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token(
TokenInfoIteratorContext* context,
TOTP_ITERATOR_UPDATE_TOKEN_ACTION update,
const void* update_context);
/**
* @brief Adds new token info to the end of the list using given update action
* @param context token info iterator context
* @param update action which is responsible to make all the necessary updates to token info
* @param update_context update action context
* @return \c true if operation succeeded; \c false otherwise
*/
TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token(
TokenInfoIteratorContext* context,
TOTP_ITERATOR_UPDATE_TOKEN_ACTION update,
const void* update_context);
/**
* @brief Remvoves current token info
* @param context token info iterator context
* @return \c true if operation succeeded; \c false otherwise
*/
bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context);
/**
* @brief Disposes token info iterator and releases all the resources
* @param context token info iterator context
*/
void totp_token_info_iterator_free(TokenInfoIteratorContext* context);
/**
* @brief Gets current token info
* @param context token info iterator context
* @return current token info
*/
const TokenInfo*
totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context);
/**
* @brief Gets current token info index
* @param context token info iterator context
* @return current token info index
*/
size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context);
/**
* @brief Gets total amount of token infos found
* @param context token info iterator context
* @return amount of token infos found
*/
size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context);
/**
* @brief Attaches token info iterator to another config file
* @param context token info iterator context
* @param config_file config file reference to attach token info iterator to
*/
void totp_token_info_iterator_attach_to_config_file(
TokenInfoIteratorContext* context,
FlipperFormat* config_file);

View File

@@ -2,7 +2,6 @@
#include <furi_hal_crypto.h>
#include <furi_hal_random.h>
#include <furi_hal_version.h>
#include "../config/config.h"
#include "../../types/common.h"
#include "memset_s.h"
@@ -62,9 +61,11 @@ uint8_t* totp_crypto_decrypt(
return decrypted_data;
}
bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
CryptoSeedIVResult
totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
CryptoSeedIVResult result;
if(plugin_state->crypto_verify_data == NULL) {
FURI_LOG_D(LOGGING_TAG, "Generating new IV");
FURI_LOG_I(LOGGING_TAG, "Generating new IV");
furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE);
}
@@ -95,9 +96,9 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t
}
}
bool result = true;
result = CryptoSeedIVResultFlagSuccess;
if(plugin_state->crypto_verify_data == NULL) {
FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data");
FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data");
plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
furi_check(plugin_state->crypto_verify_data != NULL);
plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH;
@@ -110,8 +111,7 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t
plugin_state->pin_set = pin != NULL && pin_length > 0;
result = totp_config_file_update_crypto_signatures(plugin_state) ==
TotpConfigFileUpdateSuccess;
result |= CryptoSeedIVResultFlagNewCryptoVerifyData;
}
return result;

View File

@@ -2,6 +2,26 @@
#include "../../types/plugin_state.h"
typedef uint8_t CryptoSeedIVResult;
enum CryptoSeedIVResults {
/**
* @brief IV seeding operation failed
*/
CryptoSeedIVResultFailed = 0b00,
/**
* @brief IV seeding operation succeeded
*/
CryptoSeedIVResultFlagSuccess = 0b01,
/**
* @brief As a part of IV seeding operation new crypto verify data has been generated
*/
CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10
};
/**
* @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
* @param plain_data plain data to be encrypted
@@ -35,9 +55,10 @@ uint8_t* totp_crypto_decrypt(
* @param plugin_state application state
* @param pin user's PIN
* @param pin_length user's PIN length
* @return \c true on success; \c false otherwise
* @return Results of seeding IV
*/
bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
CryptoSeedIVResult
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

View File

@@ -51,23 +51,39 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) {
dialog_message_show(plugin_state->dialogs_app, message);
dialog_message_free(message);
if(dialog_result == DialogMessageButtonRight) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
} else {
if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) {
CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0);
if(seed_result & CryptoSeedIVResultFlagSuccess &&
seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
if(!totp_config_file_update_crypto_signatures(plugin_state)) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
} else if(seed_result == CryptoSeedIVResultFailed) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
}
} else if(plugin_state->pin_set) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
} else {
if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) {
CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0);
if(seed_result & CryptoSeedIVResultFlagSuccess &&
seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
if(!totp_config_file_update_crypto_signatures(plugin_state)) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
} else if(seed_result == CryptoSeedIVResultFailed) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
if(totp_crypto_verify_key(plugin_state)) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
} else {
FURI_LOG_E(
LOGGING_TAG,
@@ -96,7 +112,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE);
if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) {
if(!totp_config_file_load(plugin_state)) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
@@ -119,15 +135,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS);
ListNode* node = plugin_state->tokens_list;
ListNode* tmp;
while(node != NULL) {
tmp = node->next;
TokenInfo* tokenInfo = node->data;
token_info_free(tokenInfo);
free(node);
node = tmp;
}
totp_config_file_close(plugin_state);
if(plugin_state->crypto_verify_data != NULL) {
free(plugin_state->crypto_verify_data);
@@ -193,8 +201,9 @@ int32_t totp_app() {
}
} else if(
plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication &&
plugin_state->current_scene != TotpSceneStandby &&
furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
}
view_port_update(view_port);

View File

@@ -0,0 +1,3 @@
#include "common.h"
const char* LOGGING_TAG = "TOTP APP";

View File

@@ -1,3 +1,3 @@
#pragma once
#define LOGGING_TAG "TOTP APP"
extern const char* LOGGING_TAG;

View File

@@ -1,17 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define TOTP_NULLABLE_STRUCT(value_type) \
typedef struct TotpNullable_##value_type { \
bool is_null; \
value_type value; \
} TotpNullable_##value_type
#define TOTP_NULLABLE_NULL(s) s.is_null = true
#define TOTP_NULLABLE_VALUE(s, v) \
s.is_null = false; \
s.value = v
TOTP_NULLABLE_STRUCT(uint16_t);

View File

@@ -4,8 +4,8 @@
#include <gui/gui.h>
#include <dialogs/dialogs.h>
#include "../features_config.h"
#include <linked_list.h>
#include "../ui/totp_scenes_enum.h"
#include "../services/config/config_file_context.h"
#include "notification_method.h"
#include "automation_method.h"
#ifdef TOTP_BADBT_TYPE_ENABLED
@@ -48,20 +48,7 @@ typedef struct {
*/
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;
ConfigFileContext* config_file_context;
/**
* @brief Encrypted well-known string data

View File

@@ -8,17 +8,15 @@
TokenInfo* token_info_alloc() {
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
furi_check(tokenInfo != NULL);
tokenInfo->algo = SHA1;
tokenInfo->digits = TOTP_6_DIGITS;
tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT;
tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE;
tokenInfo->name = furi_string_alloc();
token_info_set_defaults(tokenInfo);
return tokenInfo;
}
void token_info_free(TokenInfo* token_info) {
if(token_info == NULL) return;
free(token_info->name);
free(token_info->token);
furi_string_free(token_info->name);
free(token_info);
}
@@ -32,13 +30,13 @@ bool token_info_set_secret(
uint8_t* plain_secret;
size_t plain_secret_length;
size_t plain_secret_size;
if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) {
if(plain_token_secret_encoding == PlainTokenSecretEncodingBase32) {
plain_secret_size = token_secret_length;
plain_secret = malloc(plain_secret_size);
furi_check(plain_secret != NULL);
plain_secret_length =
base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size);
} else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) {
} else if(plain_token_secret_encoding == PlainTokenSecretEncodingBase64) {
plain_secret_length = 0;
plain_secret = base64_decode(
(const uint8_t*)plain_token_secret,
@@ -52,6 +50,10 @@ bool token_info_set_secret(
bool result;
if(plain_secret_length > 0) {
if(token_info->token != NULL) {
free(token_info->token);
}
token_info->token =
totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length);
result = true;
@@ -67,13 +69,13 @@ bool token_info_set_secret(
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
switch(digits) {
case 5:
token_info->digits = TOTP_5_DIGITS;
token_info->digits = TotpFiveDigitsCount;
return true;
case 6:
token_info->digits = TOTP_6_DIGITS;
token_info->digits = TotpSixDigitsCount;
return true;
case 8:
token_info->digits = TOTP_8_DIGITS;
token_info->digits = TotpEightDigitsCount;
return true;
default:
break;
@@ -134,22 +136,22 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
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;
token_info->automation_features |= TokenAutomationFeatureEnterAtTheEnd;
return true;
}
if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME) == 0) {
token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END;
token_info->automation_features |= TokenAutomationFeatureTabAtTheEnd;
return true;
}
if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME) == 0) {
token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER;
token_info->automation_features |= TokenAutomationFeatureTypeSlower;
return true;
}
if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) == 0) {
token_info->automation_features = TOKEN_AUTOMATION_FEATURE_NONE;
token_info->automation_features = TokenAutomationFeatureNone;
return true;
}
@@ -164,10 +166,17 @@ TokenInfo* token_info_clone(const TokenInfo* src) {
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);
clone->name = furi_string_alloc();
furi_string_set(clone->name, src->name);
return clone;
}
void token_info_set_defaults(TokenInfo* token_info) {
furi_check(token_info != NULL);
token_info->algo = SHA1;
token_info->digits = TotpSixDigitsCount;
token_info->duration = TOTP_TOKEN_DURATION_DEFAULT;
token_info->automation_features = TokenAutomationFeatureNone;
furi_string_reset(token_info->name);
}

View File

@@ -20,6 +20,8 @@
#define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab"
#define TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME "slower"
#define TOTP_TOKEN_DIGITS_MAX_COUNT (8)
typedef uint8_t TokenHashAlgo;
typedef uint8_t TokenDigitsCount;
typedef uint8_t TokenAutomationFeature;
@@ -32,22 +34,22 @@ enum TokenHashAlgos {
/**
* @brief SHA1 hashing algorithm
*/
SHA1,
SHA1 = 0,
/**
* @brief SHA256 hashing algorithm
*/
SHA256,
SHA256 = 1,
/**
* @brief SHA512 hashing algorithm
*/
SHA512,
SHA512 = 2,
/**
* @brief Algorithm used by Steam (Valve)
*/
STEAM
STEAM = 3
};
/**
@@ -55,19 +57,19 @@ enum TokenHashAlgos {
*/
enum TokenDigitsCounts {
/**
* @brief 6 digits
* @brief 5 digits
*/
TOTP_5_DIGITS = 5,
TotpFiveDigitsCount = 5,
/**
* @brief 6 digits
*/
TOTP_6_DIGITS = 6,
TotpSixDigitsCount = 6,
/**
* @brief 8 digits
*/
TOTP_8_DIGITS = 8
TotpEightDigitsCount = 8
};
/**
@@ -77,22 +79,22 @@ enum TokenAutomationFeatures {
/**
* @brief No features enabled
*/
TOKEN_AUTOMATION_FEATURE_NONE = 0b000,
TokenAutomationFeatureNone = 0b000,
/**
* @brief Press "Enter" key at the end as a part of token input automation
*/
TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END = 0b001,
TokenAutomationFeatureEnterAtTheEnd = 0b001,
/**
* @brief Press "Tab" key at the end as a part of token input automation
*/
TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END = 0b010,
TokenAutomationFeatureTabAtTheEnd = 0b010,
/**
* @brief Press keys slower and wait longer between keystrokes
*/
TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
TokenAutomationFeatureTypeSlower = 0b100
};
/**
@@ -103,16 +105,14 @@ enum PlainTokenSecretEncodings {
/**
* @brief Base32 encoding
*/
PLAIN_TOKEN_ENCODING_BASE32 = 0,
PlainTokenSecretEncodingBase32 = 0,
/**
* @brief Base64 encoding
*/
PLAIN_TOKEN_ENCODING_BASE64 = 1
PlainTokenSecretEncodingBase64 = 1
};
#define TOTP_TOKEN_DIGITS_MAX_COUNT (8)
/**
* @brief TOTP token information
*/
@@ -130,7 +130,7 @@ typedef struct {
/**
* @brief User-friendly token name
*/
char* name;
FuriString* name;
/**
* @brief Hashing algorithm
@@ -225,4 +225,10 @@ bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const Fur
* @param src instance to clone
* @return cloned instance
*/
TokenInfo* token_info_clone(const TokenInfo* src);
TokenInfo* token_info_clone(const TokenInfo* src);
/**
* @brief Sets default values to all the properties of \c token_info
* @param token_info instance to set defaults to
*/
void token_info_set_defaults(TokenInfo* token_info);

View File

@@ -5,29 +5,28 @@
#include "scenes/add_new_token/totp_scene_add_new_token.h"
#include "scenes/token_menu/totp_scene_token_menu.h"
#include "scenes/app_settings/totp_app_settings.h"
#include "scenes/standby/standby.h"
void totp_scene_director_activate_scene(
PluginState* const plugin_state,
Scene scene,
const void* context) {
void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene) {
totp_scene_director_deactivate_active_scene(plugin_state);
switch(scene) {
case TotpSceneGenerateToken:
totp_scene_generate_token_activate(plugin_state, context);
totp_scene_generate_token_activate(plugin_state);
break;
case TotpSceneAuthentication:
totp_scene_authenticate_activate(plugin_state);
break;
case TotpSceneAddNewToken:
totp_scene_add_new_token_activate(plugin_state, context);
totp_scene_add_new_token_activate(plugin_state);
break;
case TotpSceneTokenMenu:
totp_scene_token_menu_activate(plugin_state, context);
totp_scene_token_menu_activate(plugin_state);
break;
case TotpSceneAppSettings:
totp_scene_app_settings_activate(plugin_state, context);
totp_scene_app_settings_activate(plugin_state);
break;
case TotpSceneNone:
case TotpSceneStandby:
break;
default:
break;
@@ -56,6 +55,7 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state
totp_scene_app_settings_deactivate(plugin_state);
break;
case TotpSceneNone:
case TotpSceneStandby:
break;
default:
break;
@@ -81,6 +81,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_
break;
case TotpSceneNone:
break;
case TotpSceneStandby:
totp_scene_standby_render(canvas);
break;
default:
break;
}
@@ -105,6 +108,7 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con
processing = totp_scene_app_settings_handle_event(event, plugin_state);
break;
case TotpSceneNone:
case TotpSceneStandby:
break;
default:
break;

View File

@@ -11,10 +11,7 @@
* @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);
void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene);
/**
* @brief Deactivate current scene

View File

@@ -4,17 +4,18 @@
#include "../../scene_director.h"
#include "totp_input_text.h"
#include "../../../types/token_info.h"
#include <linked_list.h>
#include "../../../services/config/config.h"
#include "../../ui_controls.h"
#include "../../common_dialogs.h"
#include <roll_value.h>
#include "../../../types/nullable.h"
#include "../generate_token/totp_scene_generate_token.h"
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"};
char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"};
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_5_DIGITS, TOTP_6_DIGITS, TOTP_8_DIGITS};
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {
TotpFiveDigitsCount,
TotpSixDigitsCount,
TotpEightDigitsCount};
typedef enum {
TokenNameTextBox,
@@ -36,7 +37,6 @@ typedef struct {
InputTextSceneContext* token_secret_input_context;
InputTextSceneState* input_state;
uint32_t input_started_at;
TotpNullable_uint16_t current_token_index;
int16_t screen_y_offset;
TokenHashAlgo algo;
uint8_t digits_count_index;
@@ -44,6 +44,13 @@ typedef struct {
FuriString* duration_text;
} SceneState;
struct TotpAddContext {
SceneState* scene_state;
uint8_t* iv;
};
enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 };
static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) {
SceneState* scene_state = result->callback_data;
free(scene_state->token_name);
@@ -66,9 +73,29 @@ static void update_duration_text(SceneState* scene_state) {
furi_string_printf(scene_state->duration_text, "%d sec.", scene_state->duration);
}
void totp_scene_add_new_token_activate(
PluginState* plugin_state,
const TokenAddEditSceneContext* context) {
static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, const void* context) {
const struct TotpAddContext* context_t = context;
if(!token_info_set_secret(
tokenInfo,
context_t->scene_state->token_secret,
context_t->scene_state->token_secret_length,
PlainTokenSecretEncodingBase32,
context_t->iv)) {
return TotpIteratorUpdateTokenResultInvalidSecret;
}
furi_string_set_strn(
tokenInfo->name,
context_t->scene_state->token_name,
context_t->scene_state->token_name_length + 1);
tokenInfo->algo = context_t->scene_state->algo;
tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[context_t->scene_state->digits_count_index];
tokenInfo->duration = context_t->scene_state->duration;
return TotpIteratorUpdateTokenResultSuccess;
}
void totp_scene_add_new_token_activate(PluginState* plugin_state) {
SceneState* scene_state = malloc(sizeof(SceneState));
furi_check(scene_state != NULL);
plugin_state->current_scene_state = scene_state;
@@ -97,12 +124,6 @@ void totp_scene_add_new_token_activate(
scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT;
scene_state->duration_text = furi_string_alloc();
update_duration_text(scene_state);
if(context == NULL) {
TOTP_NULLABLE_NULL(scene_state->current_token_index);
} else {
TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index);
}
}
void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) {
@@ -260,38 +281,16 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
case TokenDurationSelect:
break;
case ConfirmButton: {
TokenInfo* tokenInfo = token_info_alloc();
bool token_secret_set = token_info_set_secret(
tokenInfo,
scene_state->token_secret,
scene_state->token_secret_length,
PLAIN_TOKEN_ENCODING_BASE32,
&plugin_state->iv[0]);
struct TotpAddContext add_context = {
.iv = plugin_state->iv, .scene_state = scene_state};
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token(
iterator_context, &add_token_handler, &add_context);
if(token_secret_set) {
tokenInfo->name = malloc(scene_state->token_name_length + 1);
furi_check(tokenInfo->name != NULL);
strlcpy(
tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1);
tokenInfo->algo = scene_state->algo;
tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[scene_state->digits_count_index];
tokenInfo->duration = scene_state->duration;
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check);
plugin_state->tokens_count++;
if(totp_config_file_save_new_token(tokenInfo) != TotpConfigFileUpdateSuccess) {
token_info_free(tokenInfo);
totp_dialogs_config_updating_error(plugin_state);
return false;
}
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);
if(add_result == TotpIteratorUpdateTokenResultSuccess) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
} else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, "Back", NULL, NULL);
dialog_message_set_text(
@@ -305,7 +304,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
dialog_message_free(message);
scene_state->selected_control = TokenSecretTextBox;
update_screen_y_offset(scene_state);
} else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
totp_dialogs_config_updating_error(plugin_state);
}
break;
}
default:
@@ -313,14 +315,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
}
break;
case InputKeyBack:
if(!scene_state->current_token_index.is_null) {
GenerateTokenSceneContext generate_scene_context = {
.current_token_index = scene_state->current_token_index.value};
totp_scene_director_activate_scene(
plugin_state, TotpSceneGenerateToken, &generate_scene_context);
} else {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
break;
default:
break;

View File

@@ -4,13 +4,7 @@
#include "../../../types/plugin_state.h"
#include "../../../types/plugin_event.h"
typedef struct {
uint16_t current_token_index;
} TokenAddEditSceneContext;
void totp_scene_add_new_token_activate(
PluginState* plugin_state,
const TokenAddEditSceneContext* context);
void totp_scene_add_new_token_activate(PluginState* plugin_state);
void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state);
bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state);
void totp_scene_add_new_token_deactivate(PluginState* plugin_state);

View File

@@ -9,7 +9,6 @@
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
#include <roll_value.h>
#include "../../../types/nullable.h"
#include "../../../features_config.h"
#ifdef TOTP_BADBT_TYPE_ENABLED
#include "../../../workers/bt_type_code/bt_type_code.h"
@@ -40,21 +39,13 @@ typedef struct {
bool badbt_enabled;
#endif
uint8_t y_offset;
TotpNullable_uint16_t current_token_index;
Control selected_control;
} SceneState;
void totp_scene_app_settings_activate(
PluginState* plugin_state,
const AppSettingsSceneContext* context) {
void totp_scene_app_settings_activate(PluginState* plugin_state) {
SceneState* scene_state = malloc(sizeof(SceneState));
furi_check(scene_state != NULL);
plugin_state->current_scene_state = scene_state;
if(context != NULL) {
TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index);
} else {
TOTP_NULLABLE_NULL(scene_state->current_token_index);
}
float off_int;
float off_dec = modff(plugin_state->timezone_offset, &off_int);
@@ -281,8 +272,7 @@ bool totp_scene_app_settings_handle_event(
AutomationMethodNone;
#endif
if(totp_config_file_update_user_settings(plugin_state) !=
TotpConfigFileUpdateSuccess) {
if(!totp_config_file_update_user_settings(plugin_state)) {
totp_dialogs_config_updating_error(plugin_state);
return false;
}
@@ -294,25 +284,11 @@ bool totp_scene_app_settings_handle_event(
}
#endif
if(!scene_state->current_token_index.is_null) {
TokenMenuSceneContext generate_scene_context = {
.current_token_index = scene_state->current_token_index.value};
totp_scene_director_activate_scene(
plugin_state, TotpSceneTokenMenu, &generate_scene_context);
} else {
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
}
break;
case InputKeyBack: {
if(!scene_state->current_token_index.is_null) {
TokenMenuSceneContext generate_scene_context = {
.current_token_index = scene_state->current_token_index.value};
totp_scene_director_activate_scene(
plugin_state, TotpSceneTokenMenu, &generate_scene_context);
} else {
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
break;
}
default:

View File

@@ -4,13 +4,7 @@
#include "../../../types/plugin_state.h"
#include "../../../types/plugin_event.h"
typedef struct {
uint16_t current_token_index;
} AppSettingsSceneContext;
void totp_scene_app_settings_activate(
PluginState* plugin_state,
const AppSettingsSceneContext* context);
void totp_scene_app_settings_activate(PluginState* plugin_state);
void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state);
bool totp_scene_app_settings_handle_event(
const PluginEvent* const event,

View File

@@ -114,12 +114,18 @@ bool totp_scene_authenticate_handle_event(
scene_state->code_length++;
}
break;
case InputKeyOk:
totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length);
case InputKeyOk: {
CryptoSeedIVResult seed_result = totp_crypto_seed_iv(
plugin_state, &scene_state->code_input[0], scene_state->code_length);
if(seed_result & CryptoSeedIVResultFlagSuccess &&
seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
totp_config_file_update_crypto_signatures(plugin_state);
}
if(totp_crypto_verify_key(plugin_state)) {
FURI_LOG_D(LOGGING_TAG, "PIN is valid");
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
} else {
FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid");
memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH);
@@ -140,6 +146,7 @@ bool totp_scene_authenticate_handle_event(
dialog_message_free(message);
}
break;
}
case InputKeyBack:
if(scene_state->code_length > 0) {
scene_state->code_input[scene_state->code_length - 1] = 0;

View File

@@ -31,9 +31,7 @@ typedef struct {
} UiPrecalculatedDimensions;
typedef struct {
uint16_t current_token_index;
char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1];
TokenInfo* current_token;
TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context;
NotificationMessage const** notification_sequence_new_token;
NotificationMessage const** notification_sequence_automation;
@@ -128,19 +126,21 @@ static const NotificationSequence*
return (NotificationSequence*)scene_state->notification_sequence_automation;
}
static void update_totp_params(PluginState* const plugin_state) {
static void update_totp_params(PluginState* const plugin_state, size_t token_index) {
SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
if(scene_state->current_token_index < plugin_state->tokens_count) {
scene_state->current_token =
list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data;
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
if(totp_token_info_iterator_go_to(iterator_context, token_index)) {
totp_generate_code_worker_notify(
scene_state->generate_code_worker_context, TotpGenerateCodeWorkerEventForceUpdate);
}
}
static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) {
uint8_t code_length = scene_state->current_token->digits;
static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin_state) {
const SceneState* scene_state = plugin_state->current_scene_state;
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
uint8_t code_length = totp_token_info_iterator_get_current_token(iterator_context)->digits;
uint8_t offset_x = scene_state->ui_precalculated_dimensions.code_offset_x;
uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width;
uint8_t offset_x_inc = scene_state->ui_precalculated_dimensions.code_offset_x_inc;
@@ -163,10 +163,18 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s
static void on_new_token_code_generated(bool time_left, void* context) {
const PluginState* plugin_state = context;
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
if(totp_token_info_iterator_get_total_count(iterator_context) == 0) {
return;
}
SceneState* scene_state = plugin_state->current_scene_state;
const TokenInfo* current_token = totp_token_info_iterator_get_current_token(iterator_context);
uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width;
scene_state->ui_precalculated_dimensions.code_total_length =
scene_state->current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels);
current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels);
scene_state->ui_precalculated_dimensions.code_offset_x =
(SCREEN_WIDTH - scene_state->ui_precalculated_dimensions.code_total_length) >> 1;
scene_state->ui_precalculated_dimensions.code_offset_x_inc =
@@ -192,43 +200,9 @@ static void on_code_lifetime_updated_generated(float code_lifetime_percent, void
PROGRESS_BAR_MARGIN;
}
void totp_scene_generate_token_activate(
PluginState* plugin_state,
const GenerateTokenSceneContext* context) {
if(!plugin_state->token_list_loaded) {
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_app, message);
dialog_message_free(message);
}
}
void totp_scene_generate_token_activate(PluginState* plugin_state) {
SceneState* scene_state = malloc(sizeof(SceneState));
furi_check(scene_state != NULL);
if(context == NULL || context->current_token_index > plugin_state->tokens_count) {
scene_state->current_token_index = 0;
} else {
scene_state->current_token_index = context->current_token_index;
}
plugin_state->current_scene_state = scene_state;
FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
@@ -254,10 +228,11 @@ void totp_scene_generate_token_activate(
scene_state->last_code_update_sync);
}
#endif
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
scene_state->generate_code_worker_context = totp_generate_code_worker_start(
scene_state->last_code,
&scene_state->current_token,
totp_token_info_iterator_get_current_token(iterator_context),
scene_state->last_code_update_sync,
plugin_state->timezone_offset,
plugin_state->iv);
@@ -270,11 +245,14 @@ void totp_scene_generate_token_activate(
&on_code_lifetime_updated_generated,
scene_state);
update_totp_params(plugin_state);
update_totp_params(
plugin_state, totp_token_info_iterator_get_current_token_index(iterator_context));
}
void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) {
if(plugin_state->tokens_count == 0) {
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
if(totp_token_info_iterator_get_total_count(iterator_context) == 0) {
canvas_draw_str_aligned(
canvas,
SCREEN_WIDTH_CENTER,
@@ -295,7 +273,9 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
canvas_set_font(canvas, FontPrimary);
uint16_t token_name_width = canvas_string_width(canvas, scene_state->current_token->name);
const char* token_name_cstr =
furi_string_get_cstr(totp_token_info_iterator_get_current_token(iterator_context)->name);
uint16_t token_name_width = canvas_string_width(canvas, token_name_cstr);
if(SCREEN_WIDTH - token_name_width > 18) {
canvas_draw_str_aligned(
canvas,
@@ -303,22 +283,17 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
SCREEN_HEIGHT_CENTER - 20,
AlignCenter,
AlignCenter,
scene_state->current_token->name);
token_name_cstr);
} else {
canvas_draw_str_aligned(
canvas,
9,
SCREEN_HEIGHT_CENTER - 20,
AlignLeft,
AlignCenter,
scene_state->current_token->name);
canvas, 9, SCREEN_HEIGHT_CENTER - 20, AlignLeft, AlignCenter, token_name_cstr);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9);
canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9);
canvas_set_color(canvas, ColorBlack);
}
draw_totp_code(canvas, scene_state);
draw_totp_code(canvas, plugin_state);
canvas_draw_box(
canvas,
@@ -326,11 +301,10 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT,
scene_state->ui_precalculated_dimensions.progress_bar_width,
PROGRESS_BAR_HEIGHT);
if(plugin_state->tokens_count > 1) {
if(totp_token_info_iterator_get_total_count(iterator_context) > 1) {
canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9);
canvas_draw_icon(
canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9);
canvas, SCREEN_WIDTH - 8, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9);
}
#ifdef TOTP_AUTOMATION_ICONS_ENABLED
@@ -351,7 +325,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
#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) {
totp_bt_type_code_worker_is_advertising(plugin_state->bt_type_code_worker_context)) {
canvas_draw_icon(
canvas,
SCREEN_WIDTH_CENTER +
@@ -379,10 +353,12 @@ bool totp_scene_generate_token_handle_event(
if(event->input.key == InputKeyDown &&
plugin_state->automation_method & AutomationMethodBadUsb) {
scene_state = (SceneState*)plugin_state->current_scene_state;
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
totp_usb_type_code_worker_notify(
scene_state->usb_type_code_worker_context,
TotpUsbTypeCodeWorkerEventType,
scene_state->current_token->automation_features);
totp_token_info_iterator_get_current_token(iterator_context)->automation_features);
notification_message(
plugin_state->notification_app,
get_notification_sequence_automation(plugin_state, scene_state));
@@ -393,10 +369,12 @@ bool totp_scene_generate_token_handle_event(
event->input.key == InputKeyUp &&
plugin_state->automation_method & AutomationMethodBadBt) {
scene_state = (SceneState*)plugin_state->current_scene_state;
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
totp_bt_type_code_worker_notify(
plugin_state->bt_type_code_worker_context,
TotpBtTypeCodeWorkerEventType,
scene_state->current_token->automation_features);
totp_token_info_iterator_get_current_token(iterator_context)->automation_features);
notification_message(
plugin_state->notification_app,
get_notification_sequence_automation(plugin_state, scene_state));
@@ -409,37 +387,43 @@ bool totp_scene_generate_token_handle_event(
return true;
}
scene_state = (SceneState*)plugin_state->current_scene_state;
switch(event->input.key) {
case InputKeyUp:
break;
case InputKeyDown:
break;
case InputKeyRight:
totp_roll_value_uint16_t(
&scene_state->current_token_index,
case InputKeyRight: {
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
size_t current_token_index =
totp_token_info_iterator_get_current_token_index(iterator_context);
totp_roll_value_size_t(
&current_token_index,
1,
0,
plugin_state->tokens_count - 1,
totp_token_info_iterator_get_total_count(iterator_context) - 1,
RollOverflowBehaviorRoll);
update_totp_params(plugin_state);
update_totp_params(plugin_state, current_token_index);
break;
case InputKeyLeft:
totp_roll_value_uint16_t(
&scene_state->current_token_index,
}
case InputKeyLeft: {
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
size_t current_token_index =
totp_token_info_iterator_get_current_token_index(iterator_context);
totp_roll_value_size_t(
&current_token_index,
-1,
0,
plugin_state->tokens_count - 1,
totp_token_info_iterator_get_total_count(iterator_context) - 1,
RollOverflowBehaviorRoll);
update_totp_params(plugin_state);
update_totp_params(plugin_state, current_token_index);
break;
}
case InputKeyOk:
if(plugin_state->tokens_count == 0) {
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL);
} else {
TokenMenuSceneContext ctx = {.current_token_index = scene_state->current_token_index};
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
break;
case InputKeyBack:
break;

View File

@@ -4,13 +4,7 @@
#include "../../../types/plugin_state.h"
#include "../../../types/plugin_event.h"
typedef struct {
uint16_t current_token_index;
} GenerateTokenSceneContext;
void totp_scene_generate_token_activate(
PluginState* plugin_state,
const GenerateTokenSceneContext* context);
void totp_scene_generate_token_activate(PluginState* plugin_state);
void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state);
bool totp_scene_generate_token_handle_event(
const PluginEvent* const event,

View File

@@ -0,0 +1,12 @@
#include "standby.h"
#include <totp_icons.h>
#include "../../constants.h"
void totp_scene_standby_render(Canvas* const canvas) {
canvas_draw_icon(canvas, SCREEN_WIDTH - 56, SCREEN_HEIGHT - 48, &I_DolphinCommon_56x48);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "CLI command");
canvas_draw_str_aligned(canvas, 5, 24, AlignLeft, AlignTop, "is running now");
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <gui/gui.h>
void totp_scene_standby_render(Canvas* const canvas);

View File

@@ -6,12 +6,10 @@
#include "../../constants.h"
#include "../../scene_director.h"
#include "../../../services/config/config.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../generate_token/totp_scene_generate_token.h"
#include "../add_new_token/totp_scene_add_new_token.h"
#include "../app_settings/totp_app_settings.h"
#include "../../../types/nullable.h"
#include <roll_value.h>
#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
@@ -21,25 +19,19 @@ typedef enum { AddNewToken, DeleteToken, AppSettings } Control;
typedef struct {
Control selected_control;
TotpNullable_uint16_t current_token_index;
} SceneState;
void totp_scene_token_menu_activate(
PluginState* plugin_state,
const TokenMenuSceneContext* context) {
void totp_scene_token_menu_activate(PluginState* plugin_state) {
SceneState* scene_state = malloc(sizeof(SceneState));
furi_check(scene_state != NULL);
plugin_state->current_scene_state = scene_state;
if(context != NULL) {
TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index);
} else {
TOTP_NULLABLE_NULL(scene_state->current_token_index);
}
}
void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) {
const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
if(scene_state->current_token_index.is_null) {
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
if(totp_token_info_iterator_get_total_count(iterator_context) == 0) {
ui_control_button_render(
canvas,
SCREEN_WIDTH_CENTER - 36,
@@ -95,22 +87,28 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
}
switch(event->input.key) {
case InputKeyUp:
case InputKeyUp: {
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
totp_roll_value_uint8_t(
&scene_state->selected_control, -1, AddNewToken, AppSettings, RollOverflowBehaviorRoll);
if(scene_state->selected_control == DeleteToken &&
scene_state->current_token_index.is_null) {
totp_token_info_iterator_get_total_count(iterator_context) == 0) {
scene_state->selected_control--;
}
break;
case InputKeyDown:
}
case InputKeyDown: {
const TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
totp_roll_value_uint8_t(
&scene_state->selected_control, 1, AddNewToken, AppSettings, RollOverflowBehaviorRoll);
if(scene_state->selected_control == DeleteToken &&
scene_state->current_token_index.is_null) {
totp_token_info_iterator_get_total_count(iterator_context) == 0) {
scene_state->selected_control++;
}
break;
}
case InputKeyRight:
break;
case InputKeyLeft:
@@ -118,14 +116,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
case InputKeyOk:
switch(scene_state->selected_control) {
case AddNewToken: {
if(scene_state->current_token_index.is_null) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL);
} else {
TokenAddEditSceneContext add_new_token_scene_context = {
.current_token_index = scene_state->current_token_index.value};
totp_scene_director_activate_scene(
plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken);
break;
}
case DeleteToken: {
@@ -142,34 +133,21 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
DialogMessageButton dialog_result =
dialog_message_show(plugin_state->dialogs_app, message);
dialog_message_free(message);
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
if(dialog_result == DialogMessageButtonRight &&
!scene_state->current_token_index.is_null) {
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);
if(totp_full_save_config_file(plugin_state) != TotpConfigFileUpdateSuccess) {
totp_token_info_iterator_get_total_count(iterator_context) > 0) {
if(!totp_token_info_iterator_remove_current_token_info(iterator_context)) {
totp_dialogs_config_updating_error(plugin_state);
return false;
}
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
}
break;
}
case AppSettings: {
if(!scene_state->current_token_index.is_null) {
AppSettingsSceneContext app_settings_context = {
.current_token_index = scene_state->current_token_index.value};
totp_scene_director_activate_scene(
plugin_state, TotpSceneAppSettings, &app_settings_context);
} else {
totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings);
break;
}
default:
@@ -177,14 +155,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
}
break;
case InputKeyBack: {
if(!scene_state->current_token_index.is_null) {
GenerateTokenSceneContext generate_scene_context = {
.current_token_index = scene_state->current_token_index.value};
totp_scene_director_activate_scene(
plugin_state, TotpSceneGenerateToken, &generate_scene_context);
} else {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
break;
}
default:

View File

@@ -4,13 +4,7 @@
#include "../../../types/plugin_state.h"
#include "../../../types/plugin_event.h"
typedef struct {
uint16_t current_token_index;
} TokenMenuSceneContext;
void totp_scene_token_menu_activate(
PluginState* plugin_state,
const TokenMenuSceneContext* context);
void totp_scene_token_menu_activate(PluginState* plugin_state);
void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state);
bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state);
void totp_scene_token_menu_deactivate(PluginState* plugin_state);

View File

@@ -34,5 +34,10 @@ enum Scenes {
/**
* @brief Scene where user can change application settings
*/
TotpSceneAppSettings
TotpSceneAppSettings,
/**
* @brief Scene which informs user that CLI command is running
*/
TotpSceneStandby
};

View File

@@ -2,13 +2,39 @@
#include <furi_hal_bt_hid.h>
#include <furi_hal_version.h>
#include <bt/bt_service/bt_i.h>
#include <furi/core/thread.h>
#include <furi/core/mutex.h>
#include <furi/core/string.h>
#include <furi/core/kernel.h>
#include <bt/bt_service/bt.h>
#include <storage/storage.h>
#include "../../types/common.h"
#include "../../types/token_info.h"
#include "../type_code_common.h"
#include "../../features_config.h"
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH
#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE
#endif
#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys")
struct TotpBtTypeCodeWorkerContext {
char* code_buffer;
uint8_t code_buffer_size;
uint8_t flags;
FuriThread* thread;
FuriMutex* code_buffer_sync;
Bt* bt;
bool is_advertising;
bool is_connected;
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN];
uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
#endif
};
static inline bool totp_type_code_worker_stop_requested() {
return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
}
@@ -197,4 +223,8 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
context->bt = NULL;
free(context);
}
bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context) {
return context->is_advertising;
}

View File

@@ -1,50 +1,79 @@
#pragma once
#include <stdlib.h>
#include <furi/core/thread.h>
#include <stdint.h>
#include <stdbool.h>
#include <furi/core/mutex.h>
#include <furi/core/string.h>
#include <furi/core/kernel.h>
#include <bt/bt_service/bt.h>
#include "../../features_config.h"
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH
#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE
#endif
typedef uint8_t TotpBtTypeCodeWorkerEvent;
typedef struct {
char* code_buffer;
uint8_t code_buffer_size;
uint8_t flags;
FuriThread* thread;
FuriMutex* code_buffer_sync;
Bt* bt;
bool is_advertising;
bool is_connected;
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN];
uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
#endif
} TotpBtTypeCodeWorkerContext;
typedef struct TotpBtTypeCodeWorkerContext TotpBtTypeCodeWorkerContext;
/**
* @brief Bluetooth token input automation worker events
*/
enum TotpBtTypeCodeWorkerEvents {
/**
* @brief Reserved, should not be used anywhere
*/
TotpBtTypeCodeWorkerEventReserved = 0b00,
/**
* @brief Stop worker
*/
TotpBtTypeCodeWorkerEventStop = 0b01,
/**
* @brief Trigger token input automation
*/
TotpBtTypeCodeWorkerEventType = 0b10
};
/**
* @brief Initializes bluetooth token input automation worker
* @return worker context
*/
TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init();
/**
* @brief Disposes bluetooth token input automation worker and releases all the allocated resources
* @param context worker context
*/
void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context);
/**
* @brief Starts bluetooth token input automation worker
* @param context worker context
* @param code_buffer code buffer to be used to automate
* @param code_buffer_size code buffer size
* @param code_buffer_sync code buffer synchronization primitive
*/
void totp_bt_type_code_worker_start(
TotpBtTypeCodeWorkerContext* context,
char* code_buffer,
uint8_t code_buffer_size,
FuriMutex* code_buffer_sync);
/**
* @brief Stops bluetooth token input automation worker
* @param context worker context
*/
void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context);
/**
* @brief Notifies bluetooth token input automation worker with a given event
* @param context worker context
* @param event event to notify worker with
* @param flags event flags
*/
void totp_bt_type_code_worker_notify(
TotpBtTypeCodeWorkerContext* context,
TotpBtTypeCodeWorkerEvent event,
uint8_t flags);
/**
* @brief Gets information whether Bluetooth is advertising now or not
* @param context worker context
* @return \c true if Bluetooth is advertising now; \c false otherwise
*/
bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context);

View File

@@ -1,4 +1,5 @@
#include "generate_totp_code.h"
#include <furi/core/thread.h>
#include "../../services/crypto/crypto.h"
#include "../../services/totp/totp.h"
#include "../../services/convert/convert.h"
@@ -7,6 +8,19 @@
#define ONE_SEC_MS (1000)
struct TotpGenerateCodeWorkerContext {
char* code_buffer;
FuriThread* thread;
FuriMutex* code_buffer_sync;
const TokenInfo* token_info;
float timezone_offset;
uint8_t* iv;
TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler;
void* on_new_code_generated_handler_context;
TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler;
void* on_code_lifetime_changed_handler_context;
};
static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
static void
@@ -93,7 +107,7 @@ static int32_t totp_generate_worker_callback(void* context) {
if(flags & TotpGenerateCodeWorkerEventStop) break;
const TokenInfo* token_info = *(t_context->token_info);
const TokenInfo* token_info = t_context->token_info;
if(token_info == NULL) {
continue;
}
@@ -127,7 +141,7 @@ static int32_t totp_generate_worker_callback(void* context) {
TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
char* code_buffer,
TokenInfo** token_info,
const TokenInfo* token_info,
FuriMutex* code_buffer_sync,
float timezone_offset,
uint8_t* iv) {

View File

@@ -1,7 +1,6 @@
#pragma once
#include <stdlib.h>
#include <furi/core/thread.h>
#include <furi/core/mutex.h>
#include "../../types/token_info.h"
@@ -10,39 +9,77 @@ typedef uint8_t TotpGenerateCodeWorkerEvent;
typedef void (*TOTP_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context);
typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context);
typedef struct {
char* code_buffer;
FuriThread* thread;
FuriMutex* code_buffer_sync;
TokenInfo** token_info;
float timezone_offset;
uint8_t* iv;
TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler;
void* on_new_code_generated_handler_context;
TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler;
void* on_code_lifetime_changed_handler_context;
} TotpGenerateCodeWorkerContext;
typedef struct TotpGenerateCodeWorkerContext TotpGenerateCodeWorkerContext;
/**
* @brief Generate token worker events
*/
enum TotGenerateCodeWorkerEvents {
/**
* @brief Reserved, should not be used anywhere
*/
TotpGenerateCodeWorkerEventReserved = 0b00,
/**
* @brief Stop worker
*/
TotpGenerateCodeWorkerEventStop = 0b01,
/**
* @brief Trigger token input automation
*/
TotpGenerateCodeWorkerEventForceUpdate = 0b10
};
/**
* @brief Starts generate code worker
* @param code_buffer code buffer to generate code to
* @param token_info token info to be used to generate code
* @param code_buffer_sync code buffer synchronization primitive
* @param timezone_offset timezone offset to be used to generate code
* @param iv initialization vector (IV) to be used to decrypt token secret
* @return worker context
*/
TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
char* code_buffer,
TokenInfo** token_info,
const TokenInfo* token_info,
FuriMutex* code_buffer_sync,
float timezone_offset,
uint8_t* iv);
/**
* @brief Stops generate code worker
* @param context worker context
*/
void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context);
/**
* @brief Notifies generate code worker with a given event
* @param context worker context
* @param event event to notify worker with
*/
void totp_generate_code_worker_notify(
TotpGenerateCodeWorkerContext* context,
TotpGenerateCodeWorkerEvent event);
/**
* @brief Sets new handler for "on new code generated" event
* @param context worker context
* @param on_new_code_generated_handler handler
* @param on_new_code_generated_handler_context handler context
*/
void totp_generate_code_worker_set_code_generated_handler(
TotpGenerateCodeWorkerContext* context,
TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler,
void* on_new_code_generated_handler_context);
/**
* @brief Sets new handler for "on code lifetime changed" event
* @param context worker context
* @param on_code_lifetime_changed_handler handler
* @param on_code_lifetime_changed_handler_context handler context
*/
void totp_generate_code_worker_set_lifetime_changed_handler(
TotpGenerateCodeWorkerContext* context,
TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler,

View File

@@ -14,7 +14,7 @@ static const uint8_t hid_number_keys[] = {
HID_KEYBOARD_Z};
static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
if(features & TokenAutomationFeatureTypeSlower) {
return 100;
}
@@ -22,7 +22,7 @@ static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
}
static uint32_t get_keypress_delay(TokenAutomationFeature features) {
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
if(features & TokenAutomationFeatureTypeSlower) {
return 60;
}
@@ -64,13 +64,13 @@ void totp_type_code_worker_execute_automation(
i++;
}
if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) {
if(features & TokenAutomationFeatureEnterAtTheEnd) {
furi_delay_ms(get_keystroke_delay(features));
totp_type_code_worker_press_key(
HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features);
}
if(features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) {
if(features & TokenAutomationFeatureTabAtTheEnd) {
furi_delay_ms(get_keystroke_delay(features));
totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features);
}

View File

@@ -4,6 +4,14 @@
typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key);
/**
* @brief Executes token input automation using given key press\release handlers
* @param key_press_fn key press handler
* @param key_release_fn key release handler
* @param code_buffer code buffer to be typed
* @param code_buffer_size code buffer size
* @param features automation features
*/
void totp_type_code_worker_execute_automation(
TOTP_AUTOMATION_KEY_HANDLER key_press_fn,
TOTP_AUTOMATION_KEY_HANDLER key_release_fn,

View File

@@ -1,9 +1,22 @@
#include "usb_type_code.h"
#include <furi_hal_usb.h>
#include <furi_hal_usb_hid.h>
#include <furi/core/thread.h>
#include <furi/core/kernel.h>
#include <furi/core/check.h>
#include "../../services/convert/convert.h"
#include "../../types/token_info.h"
#include "../type_code_common.h"
struct TotpUsbTypeCodeWorkerContext {
char* code_buffer;
uint8_t code_buffer_size;
uint8_t flags;
FuriThread* thread;
FuriMutex* code_buffer_sync;
FuriHalUsbInterface* usb_mode_prev;
};
static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) {
if(context->usb_mode_prev != NULL) {
furi_hal_usb_set_config(context->usb_mode_prev, NULL);

View File

@@ -1,35 +1,59 @@
#pragma once
#include <stdlib.h>
#include <furi/core/thread.h>
#include <stdint.h>
#include <stdbool.h>
#include <furi/core/mutex.h>
#include <furi/core/kernel.h>
#include <furi/core/check.h>
#include <furi_hal_usb.h>
typedef uint8_t TotpUsbTypeCodeWorkerEvent;
typedef struct {
char* code_buffer;
uint8_t code_buffer_size;
uint8_t flags;
FuriThread* thread;
FuriMutex* code_buffer_sync;
FuriHalUsbInterface* usb_mode_prev;
} TotpUsbTypeCodeWorkerContext;
typedef struct TotpUsbTypeCodeWorkerContext TotpUsbTypeCodeWorkerContext;
/**
* @brief USB token input automation worker events
*/
enum TotpUsbTypeCodeWorkerEvents {
/**
* @brief Reserved, should not be used anywhere
*/
TotpUsbTypeCodeWorkerEventReserved = 0b00,
/**
* @brief Stop worker
*/
TotpUsbTypeCodeWorkerEventStop = 0b01,
/**
* @brief Trigger token input automation
*/
TotpUsbTypeCodeWorkerEventType = 0b10
};
/**
* @brief Starts USB token input automation worker
* @param code_buffer code buffer to be used to automate
* @param code_buffer_size code buffer size
* @param code_buffer_sync code buffer synchronization primitive
* @return worker context
*/
TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
char* code_buffer,
uint8_t code_buffer_size,
FuriMutex* code_buffer_sync);
/**
* @brief Stops USB token input automation worker
* @param context worker context
*/
void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context);
/**
* @brief Notifies USB token input automation worker with a given event
* @param context worker context
* @param event event to notify worker with
* @param flags event flags
*/
void totp_usb_type_code_worker_notify(
TotpUsbTypeCodeWorkerContext* context,
TotpUsbTypeCodeWorkerEvent event,
uint8_t flags);
uint8_t flags);