Merge branch 'ul-dev' into xfw-dev

This commit is contained in:
Willy-JL
2023-04-28 21:43:56 +01:00
146 changed files with 9594 additions and 1801 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

View File

@@ -3,7 +3,7 @@
uint8_t counter = 0; uint8_t counter = 0;
uint8_t id_list_ds1990[25][8] = { uint8_t id_list_ds1990[18][8] = {
{0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, // код универсального ключа, для Vizit {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, // код универсального ключа, для Vizit
{0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает
{0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает
@@ -19,16 +19,9 @@ uint8_t id_list_ds1990[25][8] = {
{0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN)
{0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF
{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5
{0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni
{0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
{0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
{0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
{0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
{0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
{0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
{0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
}; };
uint8_t id_list_metakom[17][4] = { uint8_t id_list_metakom[17][4] = {
@@ -51,7 +44,7 @@ uint8_t id_list_metakom[17][4] = {
{0xCA, 0xCA, 0xCA, 0xCA}, // ?? {0xCA, 0xCA, 0xCA, 0xCA}, // ??
}; };
uint8_t id_list_cyfral[14][2] = { uint8_t id_list_cyfral[16][2] = {
{0x00, 0x00}, // Null bytes {0x00, 0x00}, // Null bytes
{0xFF, 0xFF}, // Only FF {0xFF, 0xFF}, // Only FF
{0x11, 0x11}, // Only 11 {0x11, 0x11}, // Only 11
@@ -66,6 +59,8 @@ uint8_t id_list_cyfral[14][2] = {
{0x12, 0x34}, // Incremental UID {0x12, 0x34}, // Incremental UID
{0x56, 0x34}, // Decremental UID {0x56, 0x34}, // Decremental UID
{0xCA, 0xCA}, // ?? {0xCA, 0xCA}, // ??
{0x8E, 0xC9}, // Elevator code
{0x6A, 0x50}, // VERY fresh code from smartkey
}; };
void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) {
@@ -130,7 +125,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
context->payload[6] = id_list_ds1990[context->attack_step][6]; context->payload[6] = id_list_ds1990[context->attack_step][6];
context->payload[7] = id_list_ds1990[context->attack_step][7]; context->payload[7] = id_list_ds1990[context->attack_step][7];
if(context->attack_step == 24) { if(context->attack_step == 17) {
context->attack_step = 0; context->attack_step = 0;
counter = 0; counter = 0;
context->is_attacking = false; context->is_attacking = false;
@@ -160,7 +155,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
context->payload[0] = id_list_cyfral[context->attack_step][0]; context->payload[0] = id_list_cyfral[context->attack_step][0];
context->payload[1] = id_list_cyfral[context->attack_step][1]; context->payload[1] = id_list_cyfral[context->attack_step][1];
if(context->attack_step == 13) { if(context->attack_step == 15) {
context->attack_step = 0; context->attack_step = 0;
counter = 0; counter = 0;
context->is_attacking = false; context->is_attacking = false;

View File

@@ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) {
uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, "%02X ", csn[i]); furi_string_cat_printf(csn_str, "%02X", csn[i]);
} }
bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN);

View File

@@ -20,7 +20,6 @@ App(
Lib( Lib(
name="base64", name="base64",
), ),
Lib(name="linked_list"),
Lib( Lib(
name="timezone_utils", 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) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) {
totp_cli_command_automation_handle(plugin_state, args, cli); totp_cli_command_automation_handle(plugin_state, args, cli);
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { } 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) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) {
totp_cli_command_update_handle(plugin_state, args, cli); totp_cli_command_update_handle(plugin_state, args, cli);
} else if( } else if(

View File

@@ -3,6 +3,11 @@
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include "../types/plugin_event.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) { bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) {
if(plugin_state->current_scene == TotpSceneAuthentication) { if(plugin_state->current_scene == TotpSceneAuthentication) {
TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n"); 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); furi_delay_ms(100);
} }
TOTP_CLI_DELETE_LAST_LINE(); totp_cli_delete_last_line();
if(plugin_state->current_scene == TotpSceneAuthentication || //-V560 if(plugin_state->current_scene == TotpSceneAuthentication || //-V560
plugin_state->current_scene == TotpSceneNone) { //-V560 plugin_state->current_scene == TotpSceneNone) { //-V560
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
return false; 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) { } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
size_t out_str_size = furi_string_size(out_str); size_t out_str_size = furi_string_size(out_str);
if(out_str_size > 0) { if(out_str_size > 0) {
TOTP_CLI_DELETE_LAST_CHAR(); totp_cli_delete_last_char();
furi_string_left(out_str, out_str_size - 1); furi_string_left(out_str, out_str_size - 1);
} }
} else if(c == CliSymbolAsciiCR) { } else if(c == CliSymbolAsciiCR) {
@@ -83,3 +89,35 @@ void furi_string_secure_free(FuriString* str) {
furi_string_free(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_OPTIONS "[options]"
#define DOCOPT_DEFAULT(val) "[default: " val "]" #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(format, ...) printf(format, ##__VA_ARGS__)
#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ #define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
@@ -22,11 +27,6 @@
printf("\e[0m"); \ printf("\e[0m"); \
fflush(stdout) 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, ...) \ #define TOTP_CLI_PRINTF_ERROR(format, ...) \
TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__) TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__)
#define TOTP_CLI_PRINTF_WARNING(format, ...) \ #define TOTP_CLI_PRINTF_WARNING(format, ...) \
@@ -36,24 +36,12 @@
#define TOTP_CLI_PRINTF_INFO(format, ...) \ #define TOTP_CLI_PRINTF_INFO(format, ...) \
TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__)
#define TOTP_CLI_DELETE_LAST_LINE() \ #define TOTP_CLI_LOCK_UI(plugin_state) \
TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ Scene __previous_scene = plugin_state->current_scene; \
fflush(stdout) totp_scene_director_activate_scene(plugin_state, TotpSceneStandby)
#define TOTP_CLI_DELETE_CURRENT_LINE() \ #define TOTP_CLI_UNLOCK_UI(plugin_state) \
TOTP_CLI_PRINTF("\33[2K\r"); \ totp_scene_director_activate_scene(plugin_state, __previous_scene)
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")
/** /**
* @brief Checks whether user is authenticated and entered correct PIN. * @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 * @param str instance to free
*/ */
void furi_string_secure_free(FuriString* str); 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 "add.h"
#include <stdlib.h> #include <stdlib.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include "../../../services/config/config.h" #include "../../../services/config/config.h"
#include "../../../services/convert/convert.h" #include "../../../services/convert/convert.h"
@@ -9,6 +8,77 @@
#include "../../../ui/scene_director.h" #include "../../../ui/scene_director.h"
#include "../../common_command_arguments.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() { void totp_cli_command_add_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT
", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); ", " 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) { void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
FuriString* temp_str = furi_string_alloc(); if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
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);
return; return;
} }
size_t temp_cstr_len = furi_string_size(temp_str); TokenInfoIteratorContext* iterator_context =
token_info->name = malloc(temp_cstr_len + 1); totp_config_get_token_iterator_context(plugin_state);
furi_check(token_info->name != NULL);
strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1);
// Read optional arguments TOTP_CLI_LOCK_UI(plugin_state);
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);
}
if(!parsed) { struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]};
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); TotpIteratorUpdateTokenResult add_result =
furi_string_free(temp_str); totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context);
token_info_free(token_info);
return;
}
}
// Reading token secret if(add_result == TotpIteratorUpdateTokenResultSuccess) {
furi_string_reset(temp_str); TOTP_CLI_PRINTF_SUCCESS(
TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); "Token \"%s\" has been successfully added\r\n",
if(!totp_cli_read_line(cli, temp_str, mask_user_input) || furi_string_get_cstr(
!totp_cli_ensure_authenticated(plugin_state, cli)) { totp_token_info_iterator_get_current_token(iterator_context)->name));
TOTP_CLI_DELETE_LAST_LINE(); } else if(add_result == TotpIteratorUpdateTokenResultCancelled) {
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
furi_string_secure_free(temp_str); } else if(add_result == TotpIteratorUpdateTokenResultInvalidArguments) {
token_info_free(token_info); totp_cli_print_invalid_arguments();
return; } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
}
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);
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); 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_CLI_UNLOCK_UI(plugin_state);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
} }

View File

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

View File

@@ -3,7 +3,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../services/config/config.h" #include "../../../services/config/config.h"
#include "../../cli_helpers.h" #include "../../cli_helpers.h"
#include "../../../ui/scene_director.h" #include "../../../ui/scene_director.h"
@@ -37,10 +36,13 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
return; return;
} }
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
int token_number; int token_number;
if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) { (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
return; return;
} }
@@ -51,23 +53,27 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
confirm_needed = false; confirm_needed = false;
} else { } else {
totp_cli_printf_unknown_argument(temp_str); totp_cli_printf_unknown_argument(temp_str);
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
furi_string_free(temp_str); furi_string_free(temp_str);
return; return;
} }
} }
furi_string_free(temp_str); 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; bool confirmed = !confirm_needed;
if(confirm_needed) { if(confirm_needed) {
TOTP_CLI_PRINTF_WARNING("WARNING!\r\n"); TOTP_CLI_PRINTF_WARNING("WARNING!\r\n");
TOTP_CLI_PRINTF_WARNING( TOTP_CLI_PRINTF_WARNING(
"TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n", "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"); TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n");
fflush(stdout); fflush(stdout);
char user_pick; char user_pick;
@@ -80,32 +86,21 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
} }
if(confirmed) { if(confirmed) {
if(!totp_cli_ensure_authenticated(plugin_state, cli)) { totp_cli_print_processing();
return; if(totp_token_info_iterator_remove_current_token_info(iterator_context)) {
} totp_cli_delete_last_line();
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_PRINTF_SUCCESS( 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 { } else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); totp_cli_delete_last_line();
} totp_cli_print_error_updating_config_file();
totp_token_info_iterator_go_to(iterator_context, original_token_index);
token_info_free(token_info);
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} }
} else { } else {
TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n"); 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 "details.h"
#include <stdlib.h> #include <stdlib.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include "../../../services/config/constants.h" #include "../../../services/config/constants.h"
#include "../../../services/config/config.h"
#include "../../../ui/scene_director.h"
#include "../../cli_helpers.h" #include "../../cli_helpers.h"
#include "../../common_command_arguments.h" #include "../../common_command_arguments.h"
@@ -17,21 +18,21 @@
} while(false) } while(false)
static void print_automation_features(const TokenInfo* token_info) { 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"); TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None");
return; return;
} }
bool header_printed = false; 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); 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); 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); 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; 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 || if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) { (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
return; 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("+----------------------+------------------------------+\r\n");
TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value");
TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n");
TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); 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(
TOTP_CLI_PRINTF( "| %-20s | %-28.28s |\r\n", "Name", furi_string_get_cstr(token_info->name));
"| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); TOTP_CLI_PRINTF(
TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info));
TOTP_CLI_PRINTF( TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits);
"| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); TOTP_CLI_PRINTF(
print_automation_features(token_info); "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " ");
TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); 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 "list.h"
#include <stdlib.h> #include <stdlib.h>
#include <linked_list.h>
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include "../../../services/config/constants.h" #include "../../../services/config/constants.h"
#include "../../../services/config/config.h"
#include "../../../ui/scene_director.h"
#include "../../cli_helpers.h" #include "../../cli_helpers.h"
void totp_cli_command_list_docopt_commands() { void totp_cli_command_list_docopt_commands() {
@@ -20,25 +21,36 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) {
return; 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"); TOTP_CLI_PRINTF("There are no tokens");
return; 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("+-----+---------------------------+--------+----+-----+\r\n");
TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur");
TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n");
uint16_t index = 1; for(size_t i = 0; i < total_count; i++) {
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { totp_token_info_iterator_go_to(iterator_context, i);
TokenInfo* token_info = (TokenInfo*)node->data; const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context);
TOTP_CLI_PRINTF( TOTP_CLI_PRINTF(
"| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n",
index, i + 1,
token_info->name, furi_string_get_cstr(token_info->name),
token_info_get_algo_as_cstr(token_info), token_info_get_algo_as_cstr(token_info),
token_info->digits, token_info->digits,
token_info->duration); token_info->duration);
index++; }
});
TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); 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 <stdlib.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include "../../../services/config/config.h" #include "../../../services/config/config.h"
#include "../../cli_helpers.h" #include "../../cli_helpers.h"
@@ -33,42 +32,52 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C
return; return;
} }
int token_index; int token_number;
if(!args_read_int_and_trim(args, &token_index) || token_index < 1 || TokenInfoIteratorContext* iterator_context =
token_index > plugin_state->tokens_count) { totp_config_get_token_iterator_context(plugin_state);
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); 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; 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 || if(!args_read_int_and_trim(args, &new_token_number) || new_token_number < 1 ||
new_token_index > plugin_state->tokens_count) { (size_t)new_token_number > total_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
return; return;
} }
bool activate_generate_token_scene = false; if(token_number == new_token_number) {
if(plugin_state->current_scene != TotpSceneAuthentication) { TOTP_CLI_PRINTF_ERROR("New token number matches current token number\r\n");
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); return;
activate_generate_token_scene = true;
} }
TokenInfo* token_info = NULL; TOTP_CLI_LOCK_UI(plugin_state);
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);
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( 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 { } 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_token_info_iterator_go_to(iterator_context, original_token_index);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} 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"); ", " 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; bool has_previous_method = false;
if(method & NotificationMethodSound) { if(method & NotificationMethodSound) {
TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); 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 { do {
if(!args_valid) { if(!args_valid) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
break; break;
} }
if(new_method_provided) { if(new_method_provided) {
Scene previous_scene = TotpSceneNone; TOTP_CLI_LOCK_UI(plugin_state);
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);
}
plugin_state->notification_method = new_method; plugin_state->notification_method = new_method;
if(totp_config_file_update_notification_method(new_method) == if(totp_config_file_update_notification_method(plugin_state)) {
TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF_SUCCESS("Notification method is set to "); TOTP_CLI_PRINTF_SUCCESS("Notification method is set to ");
totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS);
cli_nl(); cli_nl();
} else { } else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); totp_cli_print_error_updating_config_file();
} }
if(previous_scene != TotpSceneNone) { TOTP_CLI_UNLOCK_UI(plugin_state);
totp_scene_director_activate_scene(plugin_state, previous_scene, NULL);
}
} else { } else {
TOTP_CLI_PRINTF_INFO("Current notification method is "); TOTP_CLI_PRINTF_INFO("Current notification method is ");
totp_cli_command_notification_print_method( totp_cli_command_notification_print_method(

View File

@@ -2,7 +2,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include "../../../types/user_pin_codes.h" #include "../../../types/user_pin_codes.h"
#include "../../../services/config/config.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) { } else if(c == CliSymbolAsciiETX) {
TOTP_CLI_DELETE_CURRENT_LINE(); totp_cli_delete_current_line();
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
return false; return false;
} else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) {
if(*pin_length > 0) { if(*pin_length > 0) {
*pin_length = *pin_length - 1; *pin_length = *pin_length - 1;
pin[*pin_length] = 0; pin[*pin_length] = 0;
TOTP_CLI_DELETE_LAST_CHAR(); totp_cli_delete_last_char();
} }
} else if(c == CliSymbolAsciiCR) { } else if(c == CliSymbolAsciiCR) {
cli_nl(); 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; 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) { } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) {
do_remove = true; do_remove = true;
} else { } else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
} }
} else { } else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
} }
if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { 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 { do {
uint8_t old_iv[TOTP_IV_SIZE]; uint8_t old_iv[TOTP_IV_SIZE];
memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE);
uint8_t new_pin[TOTP_IV_SIZE]; uint8_t new_pin[TOTP_IV_SIZE];
memset(&new_pin[0], 0, TOTP_IV_SIZE);
uint8_t new_pin_length = 0; uint8_t new_pin_length = 0;
if(do_change) { if(do_change) {
if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length) || if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) {
!totp_cli_ensure_authenticated(plugin_state, cli)) {
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
break; 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); 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) { if(backup_path != NULL) {
TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path); TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path);
TOTP_CLI_PRINTF_WARNING( TOTP_CLI_PRINTF_WARNING(
@@ -134,61 +133,28 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
break; break;
} }
if(plugin_state->current_scene == TotpSceneGenerateToken) { TOTP_CLI_PRINTF("Encrypting...\r\n");
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
load_generate_token_scene = true;
}
TOTP_CLI_PRINTF("Encrypting, please wait...\r\n"); bool update_result =
totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length);
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;
}
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { totp_cli_delete_last_line();
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(); if(update_result) {
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
if(do_change) { if(do_change) {
TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n"); TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n");
} else if(do_remove) { } else if(do_remove) {
TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n"); TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n");
} }
} else { } else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); totp_cli_print_error_updating_config_file();
} }
} while(false); } while(false);
if(load_generate_token_scene) { TOTP_CLI_UNLOCK_UI(plugin_state);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
} }
furi_string_free(temp_str); furi_string_free(temp_str);

View File

@@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <furi/core/string.h> #include <furi/core/string.h>
#include "../../cli_helpers.h" #include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
#include "../../../services/config/config.h" #include "../../../services/config/config.h"
#define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES" #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"); 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( TOTP_CLI_PRINTF_WARNING(
"As a result of reset all the settings and tokens will be permanently lost.\r\n"); "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"); 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_cmpi_str(temp_str, TOTP_CLI_RESET_CONFIRMATION_KEYWORD) == 0;
furi_string_free(temp_str); furi_string_free(temp_str);
if(is_confirmed) { 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("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_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n");
totp_cli_force_close_app(event_queue); totp_cli_force_close_app(event_queue);
} else { } else {
TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); 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" #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_commands();
void totp_cli_command_reset_docopt_usage(); 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; char* strtof_endptr;
float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr); float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr);
if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) { if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
TOTP_CLI_LOCK_UI(plugin_state);
plugin_state->timezone_offset = tz; 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); TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz);
} else { } else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); 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_UNLOCK_UI(plugin_state);
} else { } else {
TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n"); TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
} }

View File

@@ -1,7 +1,6 @@
#include "update.h" #include "update.h"
#include <stdlib.h> #include <stdlib.h>
#include <lib/toolbox/args.h> #include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include "../../../services/config/config.h" #include "../../../services/config/config.h"
#include "../../../services/convert/convert.h" #include "../../../services/convert/convert.h"
@@ -11,6 +10,103 @@
#define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s" #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() { void totp_cli_command_update_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); 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"); 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) { 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)) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return; return;
} }
TokenInfoIteratorContext* iterator_context =
totp_config_get_token_iterator_context(plugin_state);
int token_number; int token_number;
if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 ||
token_number > plugin_state->tokens_count) { (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); totp_cli_print_invalid_arguments();
return; return;
} }
ListNode* list_item = list_element_at(plugin_state->tokens_list, token_number - 1); TOTP_CLI_LOCK_UI(plugin_state);
TokenInfo* existing_token_info = list_item->data;
TokenInfo* token_info = token_info_clone(existing_token_info);
// Read optional arguments size_t previous_index = totp_token_info_iterator_get_current_token_index(iterator_context);
bool mask_user_input = true; totp_token_info_iterator_go_to(iterator_context, token_number - 1);
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);
}
if(!parsed) { struct TotpUpdateContext update_context = {
TOTP_CLI_PRINT_INVALID_ARGUMENTS(); .args = args, .cli = cli, .iv = &plugin_state->iv[0]};
furi_string_free(temp_str); TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token(
token_info_free(token_info); iterator_context, &update_token_handler, &update_context);
return;
} 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; totp_token_info_iterator_go_to(iterator_context, previous_index);
if(update_token_secret) { TOTP_CLI_UNLOCK_UI(plugin_state);
// 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);
}
} }

View File

@@ -1,11 +1,11 @@
#include "common_command_arguments.h" #include "common_command_arguments.h"
#include <lib/toolbox/args.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); 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)); 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); totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX);
} else { } else {
if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) { if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) {
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; *secret_encoding = PlainTokenSecretEncodingBase32;
*parsed = true; *parsed = true;
} else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) { } else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) {
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE64; *secret_encoding = PlainTokenSecretEncodingBase64;
*parsed = true; *parsed = true;
} else { } else {
TOTP_CLI_PRINTF_ERROR( TOTP_CLI_PRINTF_ERROR(

View File

@@ -19,23 +19,29 @@
#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding"
void totp_cli_printf_unknown_argument(const FuriString* arg); void totp_cli_printf_unknown_argument(const FuriString* arg);
void totp_cli_printf_missed_argument_value(char* 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_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed);
bool totp_cli_try_read_digits( bool totp_cli_try_read_digits(
TokenInfo* token_info, TokenInfo* token_info,
const FuriString* arg, const FuriString* arg,
FuriString* args, FuriString* args,
bool* parsed); bool* parsed);
bool totp_cli_try_read_duration( bool totp_cli_try_read_duration(
TokenInfo* token_info, TokenInfo* token_info,
const FuriString* arg, const FuriString* arg,
FuriString* args, FuriString* args,
bool* parsed); bool* parsed);
bool totp_cli_try_read_automation_features( bool totp_cli_try_read_automation_features(
TokenInfo* token_info, TokenInfo* token_info,
FuriString* arg, FuriString* arg,
FuriString* args, FuriString* args,
bool* parsed); bool* parsed);
bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag);
bool totp_cli_try_read_plain_token_secret_encoding( bool totp_cli_try_read_plain_token_secret_encoding(

View File

@@ -1,6 +1,7 @@
/* /*
* Base64 encoding/decoding (RFC1341) * Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi> * 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 * 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 * 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(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 #pragma once
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
typedef uint8_t TotpRollValueOverflowBehavior; 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); 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. * When value reaches constraint value \p overflow_behavior defines what to do next.
* @param[in,out] value value to roll * @param[in,out] value value to roll
* @param step step to be used to change value * @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 max maximum possible value
* @param overflow_behavior defines what to do when value reaches constraint 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 #pragma once
#include <flipper_format/flipper_format.h>
#include "../../types/plugin_state.h" #include "../../types/plugin_state.h"
#include "../../types/token_info.h" #include "../../types/token_info.h"
#include "config_file_context.h"
#include "constants.h" #include "constants.h"
#include "token_info_iterator.h"
typedef uint8_t TokenLoadingResult;
typedef uint8_t TotpConfigFileOpenResult; typedef uint8_t TotpConfigFileOpenResult;
typedef uint8_t TotpConfigFileUpdateResult; 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 * @brief Tries to take a config file backup
* @param plugin_state application state
* @return backup path if backup successfully taken; \c NULL otherwise * @return backup path if backup successfully taken; \c NULL otherwise
*/ */
char* totp_config_file_backup(); char* totp_config_file_backup(const PluginState* plugin_state);
/**
* @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);
/** /**
* @brief Loads basic information from an application config file into application state without loading all the tokens * @brief Loads basic information from an application config file into application state without loading all the tokens
* @param plugin_state application state * @param plugin_state application state
* @return Config file open result * @return Config file open result
*/ */
TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state); bool totp_config_file_load(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);
/** /**
* @brief Updates timezone offset in an application config file * @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 * @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 * @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 * @return Config file update result
*/ */
TotpConfigFileUpdateResult bool totp_config_file_update_notification_method(const PluginState* plugin_state);
totp_config_file_update_notification_method(NotificationMethod new_notification_method);
/** /**
* @brief Updates automation method in an application config file * @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 * @return Config file update result
*/ */
TotpConfigFileUpdateResult bool totp_config_file_update_automation_method(const PluginState* plugin_state);
totp_config_file_update_automation_method(AutomationMethod new_automation_method);
/** /**
* @brief Updates application user settings * @brief Updates application user settings
* @param plugin_state application state * @param plugin_state application state
* @return Config file update result * @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 * @brief Updates crypto signatures information
* @param plugin_state application state * @param plugin_state application state
* @return Config file update result * @return Config file update result
*/ */
TotpConfigFileUpdateResult bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state);
totp_config_file_update_crypto_signatures(const PluginState* plugin_state);
/** /**
* @brief Reset all the settings to default * @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 #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_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_TIMEZONE "Timezone"
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"

View File

@@ -1,6 +1,7 @@
#include "common_migration.h" #include "common_migration.h"
#include "../constants.h" #include "../constants.h"
#include "../../../types/token_info.h" #include "../../../types/token_info.h"
#include <flipper_format/flipper_format_i.h>
bool totp_config_migrate_to_latest( bool totp_config_migrate_to_latest(
FlipperFormat* fff_data_file, FlipperFormat* fff_data_file,
@@ -57,18 +58,12 @@ bool totp_config_migrate_to_latest(
flipper_format_rewind(fff_backup_data_file); flipper_format_rewind(fff_backup_data_file);
FuriString* comment_str = furi_string_alloc();
while(true) { while(true) {
if(!flipper_format_read_string( if(!flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
break; 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_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str);
flipper_format_read_string( flipper_format_read_string(
@@ -78,15 +73,32 @@ bool totp_config_migrate_to_latest(
if(current_version > 1) { if(current_version > 1) {
flipper_format_read_string( flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); 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( flipper_format_read_string(
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); 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); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str);
} else { } else {
flipper_format_write_string_cstr( const uint32_t default_algo = SHA1;
fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); flipper_format_write_uint32(
const uint32_t default_digits = TOTP_6_DIGITS; fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &default_algo, 1);
const uint32_t default_digits = TotpSixDigitsCount;
flipper_format_write_uint32( flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1); 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( flipper_format_write_string(
fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str); fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str);
} else { } else {
const uint32_t default_automation_features = TOKEN_AUTOMATION_FEATURE_NONE; const uint32_t default_automation_features = TokenAutomationFeatureNone;
flipper_format_write_uint32( flipper_format_write_uint32(
fff_data_file, fff_data_file,
TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES,
&default_automation_features, &default_automation_features,
1); 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; result = true;
} while(false); } while(false);

View File

@@ -2,6 +2,12 @@
#include <flipper_format/flipper_format.h> #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( bool totp_config_migrate_to_latest(
FlipperFormat* fff_data_file, FlipperFormat* fff_data_file,
FlipperFormat* fff_backup_data_file); FlipperFormat* fff_backup_data_file);

View File

@@ -0,0 +1,547 @@
#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;
}
if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) {
return false;
}
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;
}
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) ||
!token_info_set_algo_from_int(tokenInfo, temp_data32)) {
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_crypto.h>
#include <furi_hal_random.h> #include <furi_hal_random.h>
#include <furi_hal_version.h> #include <furi_hal_version.h>
#include "../config/config.h"
#include "../../types/common.h" #include "../../types/common.h"
#include "memset_s.h" #include "memset_s.h"
@@ -62,9 +61,11 @@ uint8_t* totp_crypto_decrypt(
return decrypted_data; 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) { 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); 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) { 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); plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
furi_check(plugin_state->crypto_verify_data != NULL); furi_check(plugin_state->crypto_verify_data != NULL);
plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; 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; plugin_state->pin_set = pin != NULL && pin_length > 0;
result = totp_config_file_update_crypto_signatures(plugin_state) == result |= CryptoSeedIVResultFlagNewCryptoVerifyData;
TotpConfigFileUpdateSuccess;
} }
return result; return result;

View File

@@ -2,6 +2,26 @@
#include "../../types/plugin_state.h" #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) * @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
* @param plain_data plain data to be encrypted * @param plain_data plain data to be encrypted
@@ -35,9 +55,10 @@ uint8_t* totp_crypto_decrypt(
* @param plugin_state application state * @param plugin_state application state
* @param pin user's PIN * @param pin user's PIN
* @param pin_length user's PIN length * @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 * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption

View File

@@ -1,5 +1,4 @@
#include <string.h> #include <string.h>
#include "sha256.h"
#include "memxor.h" #include "memxor.h"
#define IPAD 0x36 #define IPAD 0x36

View File

@@ -15,6 +15,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */ along with this program. If not, see <https://www.gnu.org/licenses/>. */
#include "hmac_sha256.h" #include "hmac_sha256.h"
#include "sha256.h"
#define GL_HMAC_NAME 256 #define GL_HMAC_NAME 256
#define GL_HMAC_BLOCKSIZE 64 #define GL_HMAC_BLOCKSIZE 64

View File

@@ -27,6 +27,8 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "sha_pad_buffer.h"
#ifdef WORDS_BIGENDIAN #ifdef WORDS_BIGENDIAN
#define SWAP(n) (n) #define SWAP(n) (n)
#else #else
@@ -34,10 +36,6 @@
#define SWAP(n) swap_uint32(n) #define SWAP(n) swap_uint32(n)
#endif #endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (RFC 1321, 3.1: Step 1) */
static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */};
/* Take a pointer to a 160 bit block of data (five 32 bit ints) and /* Take a pointer to a 160 bit block of data (five 32 bit ints) and
initialize it to the start constants of the SHA1 algorithm. This initialize it to the start constants of the SHA1 algorithm. This
must be called before using hash in the call to sha1_hash. */ must be called before using hash in the call to sha1_hash. */
@@ -87,7 +85,7 @@ void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) {
ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29));
ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3); ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3);
memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes);
/* Process last bytes. */ /* Process last bytes. */
sha1_process_block(ctx->buffer, size * 4, ctx); sha1_process_block(ctx->buffer, size * 4, ctx);

View File

@@ -25,6 +25,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "sha_pad_buffer.h"
#ifdef WORDS_BIGENDIAN #ifdef WORDS_BIGENDIAN
#define SWAP(n) (n) #define SWAP(n) (n)
@@ -33,10 +34,6 @@
#define SWAP(n) swap_uint32(n) #define SWAP(n) swap_uint32(n)
#endif #endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. */
static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */};
/* /*
Takes a pointer to a 256 bit block of data (eight 32 bit ints) and Takes a pointer to a 256 bit block of data (eight 32 bit ints) and
initializes it to the start constants of the SHA256 algorithm. This initializes it to the start constants of the SHA256 algorithm. This
@@ -91,7 +88,7 @@ static void sha256_conclude_ctx(struct sha256_ctx* ctx) {
set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29))); set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3)); set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3));
memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes);
/* Process last bytes. */ /* Process last bytes. */
sha256_process_block(ctx->buffer, size * 4, ctx); sha256_process_block(ctx->buffer, size * 4, ctx);

View File

@@ -27,13 +27,10 @@
#include <string.h> #include <string.h>
#include "byteswap.h" #include "byteswap.h"
#include "sha_pad_buffer.h"
#define SWAP(n) swap_uint64(n) #define SWAP(n) swap_uint64(n)
/* This array contains the bytes used to pad the buffer to the next
128-byte boundary. */
static const unsigned char fillbuf[128] = {0x80, 0 /* , 0, 0, ... */};
/* /*
Takes a pointer to a 512 bit block of data (eight 64 bit ints) and Takes a pointer to a 512 bit block of data (eight 64 bit ints) and
initializes it to the start constants of the SHA512 algorithm. This initializes it to the start constants of the SHA512 algorithm. This
@@ -90,7 +87,7 @@ static void sha512_conclude_ctx(struct sha512_ctx* ctx) {
SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61)))); SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61))));
set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3))); set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3)));
memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes); sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 8 - bytes);
/* Process last bytes. */ /* Process last bytes. */
sha512_process_block(ctx->buffer, size * 8, ctx); sha512_process_block(ctx->buffer, size * 8, ctx);

View File

@@ -0,0 +1,11 @@
#include "sha_pad_buffer.h"
#include <string.h>
void sha_pad_buffer(uint8_t* buffer, size_t size) {
if(size > 0) {
buffer[0] = 0x80;
if(size > 1) {
memset(&buffer[1], 0, size - 1);
}
}
}

View File

@@ -0,0 +1,4 @@
#include <stddef.h>
#include <stdint.h>
void sha_pad_buffer(uint8_t* buffer, size_t size);

View File

@@ -50,23 +50,39 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) {
dialog_message_show(plugin_state->dialogs_app, message); dialog_message_show(plugin_state->dialogs_app, message);
dialog_message_free(message); dialog_message_free(message);
if(dialog_result == DialogMessageButtonRight) { if(dialog_result == DialogMessageButtonRight) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
} else { } 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); totp_dialogs_config_loading_error(plugin_state);
return false; 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) { } else if(plugin_state->pin_set) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
} else { } 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); totp_dialogs_config_loading_error(plugin_state);
return false; return false;
} }
if(totp_crypto_verify_key(plugin_state)) { 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 { } else {
FURI_LOG_E( FURI_LOG_E(
LOGGING_TAG, LOGGING_TAG,
@@ -95,7 +111,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); 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); totp_dialogs_config_loading_error(plugin_state);
return false; return false;
} }
@@ -118,15 +134,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_DIALOGS);
ListNode* node = plugin_state->tokens_list; totp_config_file_close(plugin_state);
ListNode* tmp;
while(node != NULL) {
tmp = node->next;
TokenInfo* tokenInfo = node->data;
token_info_free(tokenInfo);
free(node);
node = tmp;
}
if(plugin_state->crypto_verify_data != NULL) { if(plugin_state->crypto_verify_data != NULL) {
free(plugin_state->crypto_verify_data); free(plugin_state->crypto_verify_data);
@@ -189,8 +197,9 @@ int32_t totp_app() {
} }
} else if( } else if(
plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication &&
plugin_state->current_scene != TotpSceneStandby &&
furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { 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_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 #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 <gui/gui.h>
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include "../features_config.h" #include "../features_config.h"
#include <linked_list.h>
#include "../ui/totp_scenes_enum.h" #include "../ui/totp_scenes_enum.h"
#include "../services/config/config_file_context.h"
#include "notification_method.h" #include "notification_method.h"
#include "automation_method.h" #include "automation_method.h"
#ifdef TOTP_BADBT_TYPE_ENABLED #ifdef TOTP_BADBT_TYPE_ENABLED
@@ -48,20 +48,7 @@ typedef struct {
*/ */
float timezone_offset; float timezone_offset;
/** ConfigFileContext* config_file_context;
* @brief Token list head node
*/
ListNode* tokens_list;
/**
* @brief Whether token list is loaded or not
*/
bool token_list_loaded;
/**
* @brief Tokens list length
*/
uint16_t tokens_count;
/** /**
* @brief Encrypted well-known string data * @brief Encrypted well-known string data

View File

@@ -8,17 +8,15 @@
TokenInfo* token_info_alloc() { TokenInfo* token_info_alloc() {
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
furi_check(tokenInfo != NULL); furi_check(tokenInfo != NULL);
tokenInfo->algo = SHA1; tokenInfo->name = furi_string_alloc();
tokenInfo->digits = TOTP_6_DIGITS; token_info_set_defaults(tokenInfo);
tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT;
tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE;
return tokenInfo; return tokenInfo;
} }
void token_info_free(TokenInfo* token_info) { void token_info_free(TokenInfo* token_info) {
if(token_info == NULL) return; if(token_info == NULL) return;
free(token_info->name);
free(token_info->token); free(token_info->token);
furi_string_free(token_info->name);
free(token_info); free(token_info);
} }
@@ -32,13 +30,13 @@ bool token_info_set_secret(
uint8_t* plain_secret; uint8_t* plain_secret;
size_t plain_secret_length; size_t plain_secret_length;
size_t plain_secret_size; 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_size = token_secret_length;
plain_secret = malloc(plain_secret_size); plain_secret = malloc(plain_secret_size);
furi_check(plain_secret != NULL); furi_check(plain_secret != NULL);
plain_secret_length = plain_secret_length =
base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size); 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_length = 0;
plain_secret = base64_decode( plain_secret = base64_decode(
(const uint8_t*)plain_token_secret, (const uint8_t*)plain_token_secret,
@@ -52,6 +50,10 @@ bool token_info_set_secret(
bool result; bool result;
if(plain_secret_length > 0) { if(plain_secret_length > 0) {
if(token_info->token != NULL) {
free(token_info->token);
}
token_info->token = token_info->token =
totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length);
result = true; result = true;
@@ -67,13 +69,13 @@ bool token_info_set_secret(
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
switch(digits) { switch(digits) {
case 5: case 5:
token_info->digits = TOTP_5_DIGITS; token_info->digits = TotpFiveDigitsCount;
return true; return true;
case 6: case 6:
token_info->digits = TOTP_6_DIGITS; token_info->digits = TotpSixDigitsCount;
return true; return true;
case 8: case 8:
token_info->digits = TOTP_8_DIGITS; token_info->digits = TotpEightDigitsCount;
return true; return true;
default: default:
break; break;
@@ -115,6 +117,27 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str)
return false; return false;
} }
bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code) {
switch(algo_code) {
case SHA1:
token_info->algo = SHA1;
break;
case SHA256:
token_info->algo = SHA256;
break;
case SHA512:
token_info->algo = SHA512;
break;
case STEAM:
token_info->algo = STEAM;
break;
default:
return false;
}
return true;
}
char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
switch(token_info->algo) { switch(token_info->algo) {
case SHA1: case SHA1:
@@ -134,22 +157,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) { 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) { 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; return true;
} }
if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME) == 0) { 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; return true;
} }
if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME) == 0) { 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; return true;
} }
if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) == 0) { 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; return true;
} }
@@ -164,10 +187,17 @@ TokenInfo* token_info_clone(const TokenInfo* src) {
furi_check(clone->token != NULL); furi_check(clone->token != NULL);
memcpy(clone->token, src->token, src->token_length); memcpy(clone->token, src->token, src->token_length);
int name_length = strnlen(src->name, TOTP_TOKEN_MAX_LENGTH); clone->name = furi_string_alloc();
clone->name = malloc(name_length + 1); furi_string_set(clone->name, src->name);
furi_check(clone->name != NULL);
strlcpy(clone->name, src->name, name_length + 1);
return clone; 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_TAB_AT_THE_END_NAME "tab"
#define TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME "slower" #define TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME "slower"
#define TOTP_TOKEN_DIGITS_MAX_COUNT (8)
typedef uint8_t TokenHashAlgo; typedef uint8_t TokenHashAlgo;
typedef uint8_t TokenDigitsCount; typedef uint8_t TokenDigitsCount;
typedef uint8_t TokenAutomationFeature; typedef uint8_t TokenAutomationFeature;
@@ -32,22 +34,22 @@ enum TokenHashAlgos {
/** /**
* @brief SHA1 hashing algorithm * @brief SHA1 hashing algorithm
*/ */
SHA1, SHA1 = 0,
/** /**
* @brief SHA256 hashing algorithm * @brief SHA256 hashing algorithm
*/ */
SHA256, SHA256 = 1,
/** /**
* @brief SHA512 hashing algorithm * @brief SHA512 hashing algorithm
*/ */
SHA512, SHA512 = 2,
/** /**
* @brief Algorithm used by Steam (Valve) * @brief Algorithm used by Steam (Valve)
*/ */
STEAM STEAM = 3
}; };
/** /**
@@ -55,19 +57,19 @@ enum TokenHashAlgos {
*/ */
enum TokenDigitsCounts { enum TokenDigitsCounts {
/** /**
* @brief 6 digits * @brief 5 digits
*/ */
TOTP_5_DIGITS = 5, TotpFiveDigitsCount = 5,
/** /**
* @brief 6 digits * @brief 6 digits
*/ */
TOTP_6_DIGITS = 6, TotpSixDigitsCount = 6,
/** /**
* @brief 8 digits * @brief 8 digits
*/ */
TOTP_8_DIGITS = 8 TotpEightDigitsCount = 8
}; };
/** /**
@@ -77,22 +79,22 @@ enum TokenAutomationFeatures {
/** /**
* @brief No features enabled * @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 * @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 * @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 * @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 * @brief Base32 encoding
*/ */
PLAIN_TOKEN_ENCODING_BASE32 = 0, PlainTokenSecretEncodingBase32 = 0,
/** /**
* @brief Base64 encoding * @brief Base64 encoding
*/ */
PLAIN_TOKEN_ENCODING_BASE64 = 1 PlainTokenSecretEncodingBase64 = 1
}; };
#define TOTP_TOKEN_DIGITS_MAX_COUNT (8)
/** /**
* @brief TOTP token information * @brief TOTP token information
*/ */
@@ -130,7 +130,7 @@ typedef struct {
/** /**
* @brief User-friendly token name * @brief User-friendly token name
*/ */
char* name; FuriString* name;
/** /**
* @brief Hashing algorithm * @brief Hashing algorithm
@@ -168,7 +168,7 @@ void token_info_free(TokenInfo* token_info);
/** /**
* @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo * @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo
* @param token_info instance where secret should be updated * @param token_info instance where secret should be updated
* @param base32_token_secret plain token secret in Base32 format * @param plain_token_secret plain token secret
* @param token_secret_length plain token secret length * @param token_secret_length plain token secret length
* @param plain_token_secret_encoding plain token secret encoding * @param plain_token_secret_encoding plain token secret encoding
* @param iv initialization vecor (IV) to be used for encryption * @param iv initialization vecor (IV) to be used for encryption
@@ -201,10 +201,18 @@ bool token_info_set_duration_from_int(TokenInfo* token_info, uint8_t duration);
* @brief Sets token hashing algorithm from \c str value * @brief Sets token hashing algorithm from \c str value
* @param token_info instance whichs token hashing algorithm should be updated * @param token_info instance whichs token hashing algorithm should be updated
* @param str desired token algorithm * @param str desired token algorithm
* @return \c true if token hahsing algorithm has been updated; \c false otherwise * @return \c true if token hashing algorithm has been updated; \c false otherwise
*/ */
bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str); bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str);
/**
* @brief Sets token hashing algorithm from \c algo_code code
* @param token_info instance whichs token hashing algorithm should be updated
* @param algo_code desired token algorithm code
* @return \c true if token hashing algorithm has been updated; \c false otherwise
*/
bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code);
/** /**
* @brief Gets token hahsing algorithm name as C-string * @brief Gets token hahsing algorithm name as C-string
* @param token_info instance which token hahsing algorithm name should be returned * @param token_info instance which token hahsing algorithm name should be returned
@@ -226,3 +234,9 @@ bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const Fur
* @return cloned instance * @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/add_new_token/totp_scene_add_new_token.h"
#include "scenes/token_menu/totp_scene_token_menu.h" #include "scenes/token_menu/totp_scene_token_menu.h"
#include "scenes/app_settings/totp_app_settings.h" #include "scenes/app_settings/totp_app_settings.h"
#include "scenes/standby/standby.h"
void totp_scene_director_activate_scene( void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene) {
PluginState* const plugin_state,
Scene scene,
const void* context) {
totp_scene_director_deactivate_active_scene(plugin_state); totp_scene_director_deactivate_active_scene(plugin_state);
switch(scene) { switch(scene) {
case TotpSceneGenerateToken: case TotpSceneGenerateToken:
totp_scene_generate_token_activate(plugin_state, context); totp_scene_generate_token_activate(plugin_state);
break; break;
case TotpSceneAuthentication: case TotpSceneAuthentication:
totp_scene_authenticate_activate(plugin_state); totp_scene_authenticate_activate(plugin_state);
break; break;
case TotpSceneAddNewToken: case TotpSceneAddNewToken:
totp_scene_add_new_token_activate(plugin_state, context); totp_scene_add_new_token_activate(plugin_state);
break; break;
case TotpSceneTokenMenu: case TotpSceneTokenMenu:
totp_scene_token_menu_activate(plugin_state, context); totp_scene_token_menu_activate(plugin_state);
break; break;
case TotpSceneAppSettings: case TotpSceneAppSettings:
totp_scene_app_settings_activate(plugin_state, context); totp_scene_app_settings_activate(plugin_state);
break; break;
case TotpSceneNone: case TotpSceneNone:
case TotpSceneStandby:
break; break;
default: default:
break; break;
@@ -56,6 +55,7 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state
totp_scene_app_settings_deactivate(plugin_state); totp_scene_app_settings_deactivate(plugin_state);
break; break;
case TotpSceneNone: case TotpSceneNone:
case TotpSceneStandby:
break; break;
default: default:
break; break;
@@ -81,6 +81,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_
break; break;
case TotpSceneNone: case TotpSceneNone:
break; break;
case TotpSceneStandby:
totp_scene_standby_render(canvas);
break;
default: default:
break; 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); processing = totp_scene_app_settings_handle_event(event, plugin_state);
break; break;
case TotpSceneNone: case TotpSceneNone:
case TotpSceneStandby:
break; break;
default: default:
break; break;

View File

@@ -11,10 +11,7 @@
* @param scene scene to be activated * @param scene scene to be activated
* @param context scene context to be passed to the scene activation method * @param context scene context to be passed to the scene activation method
*/ */
void totp_scene_director_activate_scene( void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene);
PluginState* const plugin_state,
Scene scene,
const void* context);
/** /**
* @brief Deactivate current scene * @brief Deactivate current scene

View File

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

View File

@@ -4,13 +4,7 @@
#include "../../../types/plugin_state.h" #include "../../../types/plugin_state.h"
#include "../../../types/plugin_event.h" #include "../../../types/plugin_event.h"
typedef struct { void totp_scene_add_new_token_activate(PluginState* plugin_state);
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_render(Canvas* const canvas, 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); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state);
void totp_scene_add_new_token_deactivate(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/config/config.h"
#include "../../../services/convert/convert.h" #include "../../../services/convert/convert.h"
#include <roll_value.h> #include <roll_value.h>
#include "../../../types/nullable.h"
#include "../../../features_config.h" #include "../../../features_config.h"
#ifdef TOTP_BADBT_TYPE_ENABLED #ifdef TOTP_BADBT_TYPE_ENABLED
#include "../../../workers/bt_type_code/bt_type_code.h" #include "../../../workers/bt_type_code/bt_type_code.h"
@@ -40,21 +39,13 @@ typedef struct {
bool badbt_enabled; bool badbt_enabled;
#endif #endif
uint8_t y_offset; uint8_t y_offset;
TotpNullable_uint16_t current_token_index;
Control selected_control; Control selected_control;
} SceneState; } SceneState;
void totp_scene_app_settings_activate( void totp_scene_app_settings_activate(PluginState* plugin_state) {
PluginState* plugin_state,
const AppSettingsSceneContext* context) {
SceneState* scene_state = malloc(sizeof(SceneState)); SceneState* scene_state = malloc(sizeof(SceneState));
furi_check(scene_state != NULL); furi_check(scene_state != NULL);
plugin_state->current_scene_state = scene_state; 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_int;
float off_dec = modff(plugin_state->timezone_offset, &off_int); float off_dec = modff(plugin_state->timezone_offset, &off_int);
@@ -281,8 +272,7 @@ bool totp_scene_app_settings_handle_event(
AutomationMethodNone; AutomationMethodNone;
#endif #endif
if(totp_config_file_update_user_settings(plugin_state) != if(!totp_config_file_update_user_settings(plugin_state)) {
TotpConfigFileUpdateSuccess) {
totp_dialogs_config_updating_error(plugin_state); totp_dialogs_config_updating_error(plugin_state);
return false; return false;
} }
@@ -294,25 +284,11 @@ bool totp_scene_app_settings_handle_event(
} }
#endif #endif
if(!scene_state->current_token_index.is_null) { totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
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);
}
} }
break; break;
case InputKeyBack: { case InputKeyBack: {
if(!scene_state->current_token_index.is_null) { totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
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);
}
break; break;
} }
default: default:

View File

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

View File

@@ -114,12 +114,18 @@ bool totp_scene_authenticate_handle_event(
scene_state->code_length++; scene_state->code_length++;
} }
break; break;
case InputKeyOk: case InputKeyOk: {
totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length); 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)) { if(totp_crypto_verify_key(plugin_state)) {
FURI_LOG_D(LOGGING_TAG, "PIN is valid"); 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 { } else {
FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid");
memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH);
@@ -140,6 +146,7 @@ bool totp_scene_authenticate_handle_event(
dialog_message_free(message); dialog_message_free(message);
} }
break; break;
}
case InputKeyBack: case InputKeyBack:
if(scene_state->code_length > 0) { if(scene_state->code_length > 0) {
scene_state->code_input[scene_state->code_length - 1] = 0; scene_state->code_input[scene_state->code_length - 1] = 0;

View File

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

View File

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

View File

@@ -4,13 +4,7 @@
#include "../../../types/plugin_state.h" #include "../../../types/plugin_state.h"
#include "../../../types/plugin_event.h" #include "../../../types/plugin_event.h"
typedef struct { void totp_scene_token_menu_activate(PluginState* plugin_state);
uint16_t current_token_index;
} TokenMenuSceneContext;
void totp_scene_token_menu_activate(
PluginState* plugin_state,
const TokenMenuSceneContext* context);
void totp_scene_token_menu_render(Canvas* const canvas, 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); bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state);
void totp_scene_token_menu_deactivate(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 * @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_bt_hid.h>
#include <furi_hal_version.h> #include <furi_hal_version.h>
#include <bt/bt_service/bt_i.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 <storage/storage.h>
#include "../../types/common.h" #include "../../types/common.h"
#include "../../types/token_info.h" #include "../../types/token_info.h"
#include "../type_code_common.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") #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() { static inline bool totp_type_code_worker_stop_requested() {
return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
} }
@@ -198,3 +224,7 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
free(context); 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 #pragma once
#include <stdlib.h> #include <stdint.h>
#include <furi/core/thread.h> #include <stdbool.h>
#include <furi/core/mutex.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 uint8_t TotpBtTypeCodeWorkerEvent;
typedef struct { typedef struct TotpBtTypeCodeWorkerContext 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
} TotpBtTypeCodeWorkerContext;
/**
* @brief Bluetooth token input automation worker events
*/
enum TotpBtTypeCodeWorkerEvents { enum TotpBtTypeCodeWorkerEvents {
/**
* @brief Reserved, should not be used anywhere
*/
TotpBtTypeCodeWorkerEventReserved = 0b00, TotpBtTypeCodeWorkerEventReserved = 0b00,
/**
* @brief Stop worker
*/
TotpBtTypeCodeWorkerEventStop = 0b01, TotpBtTypeCodeWorkerEventStop = 0b01,
/**
* @brief Trigger token input automation
*/
TotpBtTypeCodeWorkerEventType = 0b10 TotpBtTypeCodeWorkerEventType = 0b10
}; };
/**
* @brief Initializes bluetooth token input automation worker
* @return worker context
*/
TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); 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); 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( void totp_bt_type_code_worker_start(
TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerContext* context,
char* code_buffer, char* code_buffer,
uint8_t code_buffer_size, uint8_t code_buffer_size,
FuriMutex* code_buffer_sync); FuriMutex* code_buffer_sync);
/**
* @brief Stops bluetooth token input automation worker
* @param context worker context
*/
void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* 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( void totp_bt_type_code_worker_notify(
TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerContext* context,
TotpBtTypeCodeWorkerEvent event, TotpBtTypeCodeWorkerEvent event,
uint8_t flags); 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 "generate_totp_code.h"
#include <furi/core/thread.h>
#include "../../services/crypto/crypto.h" #include "../../services/crypto/crypto.h"
#include "../../services/totp/totp.h" #include "../../services/totp/totp.h"
#include "../../services/convert/convert.h" #include "../../services/convert/convert.h"
@@ -7,6 +8,19 @@
#define ONE_SEC_MS (1000) #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 const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
static void static void
@@ -93,7 +107,7 @@ static int32_t totp_generate_worker_callback(void* context) {
if(flags & TotpGenerateCodeWorkerEventStop) break; if(flags & TotpGenerateCodeWorkerEventStop) break;
const TokenInfo* token_info = *(t_context->token_info); const TokenInfo* token_info = t_context->token_info;
if(token_info == NULL) { if(token_info == NULL) {
continue; continue;
} }
@@ -127,7 +141,7 @@ static int32_t totp_generate_worker_callback(void* context) {
TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
char* code_buffer, char* code_buffer,
TokenInfo** token_info, const TokenInfo* token_info,
FuriMutex* code_buffer_sync, FuriMutex* code_buffer_sync,
float timezone_offset, float timezone_offset,
uint8_t* iv) { uint8_t* iv) {

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include <stdlib.h> #include <stdlib.h>
#include <furi/core/thread.h>
#include <furi/core/mutex.h> #include <furi/core/mutex.h>
#include "../../types/token_info.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_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context);
typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context); typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context);
typedef struct { typedef struct TotpGenerateCodeWorkerContext TotpGenerateCodeWorkerContext;
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;
/**
* @brief Generate token worker events
*/
enum TotGenerateCodeWorkerEvents { enum TotGenerateCodeWorkerEvents {
/**
* @brief Reserved, should not be used anywhere
*/
TotpGenerateCodeWorkerEventReserved = 0b00, TotpGenerateCodeWorkerEventReserved = 0b00,
/**
* @brief Stop worker
*/
TotpGenerateCodeWorkerEventStop = 0b01, TotpGenerateCodeWorkerEventStop = 0b01,
/**
* @brief Trigger token input automation
*/
TotpGenerateCodeWorkerEventForceUpdate = 0b10 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( TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
char* code_buffer, char* code_buffer,
TokenInfo** token_info, const TokenInfo* token_info,
FuriMutex* code_buffer_sync, FuriMutex* code_buffer_sync,
float timezone_offset, float timezone_offset,
uint8_t* iv); uint8_t* iv);
/**
* @brief Stops generate code worker
* @param context worker context
*/
void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* 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( void totp_generate_code_worker_notify(
TotpGenerateCodeWorkerContext* context, TotpGenerateCodeWorkerContext* context,
TotpGenerateCodeWorkerEvent event); 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( void totp_generate_code_worker_set_code_generated_handler(
TotpGenerateCodeWorkerContext* context, TotpGenerateCodeWorkerContext* context,
TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler,
void* on_new_code_generated_handler_context); 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( void totp_generate_code_worker_set_lifetime_changed_handler(
TotpGenerateCodeWorkerContext* context, TotpGenerateCodeWorkerContext* context,
TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, 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}; HID_KEYBOARD_Z};
static uint32_t get_keystroke_delay(TokenAutomationFeature features) { static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { if(features & TokenAutomationFeatureTypeSlower) {
return 100; return 100;
} }
@@ -22,7 +22,7 @@ static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
} }
static uint32_t get_keypress_delay(TokenAutomationFeature features) { static uint32_t get_keypress_delay(TokenAutomationFeature features) {
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { if(features & TokenAutomationFeatureTypeSlower) {
return 60; return 60;
} }
@@ -64,13 +64,13 @@ void totp_type_code_worker_execute_automation(
i++; i++;
} }
if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { if(features & TokenAutomationFeatureEnterAtTheEnd) {
furi_delay_ms(get_keystroke_delay(features)); furi_delay_ms(get_keystroke_delay(features));
totp_type_code_worker_press_key( totp_type_code_worker_press_key(
HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features); 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)); furi_delay_ms(get_keystroke_delay(features));
totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, 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); 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( void totp_type_code_worker_execute_automation(
TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_press_fn,
TOTP_AUTOMATION_KEY_HANDLER key_release_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn,

View File

@@ -1,9 +1,22 @@
#include "usb_type_code.h" #include "usb_type_code.h"
#include <furi_hal_usb.h>
#include <furi_hal_usb_hid.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 "../../services/convert/convert.h"
#include "../../types/token_info.h" #include "../../types/token_info.h"
#include "../type_code_common.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) { static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) {
if(context->usb_mode_prev != NULL) { if(context->usb_mode_prev != NULL) {
furi_hal_usb_set_config(context->usb_mode_prev, NULL); furi_hal_usb_set_config(context->usb_mode_prev, NULL);

View File

@@ -1,34 +1,58 @@
#pragma once #pragma once
#include <stdlib.h> #include <stdint.h>
#include <furi/core/thread.h> #include <stdbool.h>
#include <furi/core/mutex.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 uint8_t TotpUsbTypeCodeWorkerEvent;
typedef struct { typedef struct TotpUsbTypeCodeWorkerContext TotpUsbTypeCodeWorkerContext;
char* code_buffer;
uint8_t code_buffer_size;
uint8_t flags;
FuriThread* thread;
FuriMutex* code_buffer_sync;
FuriHalUsbInterface* usb_mode_prev;
} TotpUsbTypeCodeWorkerContext;
/**
* @brief USB token input automation worker events
*/
enum TotpUsbTypeCodeWorkerEvents { enum TotpUsbTypeCodeWorkerEvents {
/**
* @brief Reserved, should not be used anywhere
*/
TotpUsbTypeCodeWorkerEventReserved = 0b00, TotpUsbTypeCodeWorkerEventReserved = 0b00,
/**
* @brief Stop worker
*/
TotpUsbTypeCodeWorkerEventStop = 0b01, TotpUsbTypeCodeWorkerEventStop = 0b01,
/**
* @brief Trigger token input automation
*/
TotpUsbTypeCodeWorkerEventType = 0b10 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( TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
char* code_buffer, char* code_buffer,
uint8_t code_buffer_size, uint8_t code_buffer_size,
FuriMutex* code_buffer_sync); FuriMutex* code_buffer_sync);
/**
* @brief Stops USB token input automation worker
* @param context worker context
*/
void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* 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( void totp_usb_type_code_worker_notify(
TotpUsbTypeCodeWorkerContext* context, TotpUsbTypeCodeWorkerContext* context,
TotpUsbTypeCodeWorkerEvent event, TotpUsbTypeCodeWorkerEvent event,

View File

@@ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
&ws_protocol_auriol_th, &ws_protocol_auriol_th,
&ws_protocol_oregon_v1, &ws_protocol_oregon_v1,
&ws_protocol_tx_8300, &ws_protocol_tx_8300,
&ws_protocol_wendox_w6726,
}; };
const SubGhzProtocolRegistry weather_station_protocol_registry = { const SubGhzProtocolRegistry weather_station_protocol_registry = {

View File

@@ -16,5 +16,6 @@
#include "auriol_hg0601a.h" #include "auriol_hg0601a.h"
#include "oregon_v1.h" #include "oregon_v1.h"
#include "tx_8300.h" #include "tx_8300.h"
#include "wendox_w6726.h"
extern const SubGhzProtocolRegistry weather_station_protocol_registry; extern const SubGhzProtocolRegistry weather_station_protocol_registry;

View File

@@ -0,0 +1,299 @@
#include "wendox_w6726.h"
#define TAG "WSProtocolWendoxW6726"
/*
* Wendox W6726
*
* Temperature -50°Ñ to +70°Ñ
* _ _ _ __ _
* _| |___| |___| |___ ... | |_| |__...._______________
* preamble data guard time
*
* 3 reps every 3 minutes
* in the first message 11 bytes of the preamble in the rest by 7
*
* bit 0: 1955-hi, 5865-lo
* bit 1: 5865-hi, 1955-lo
* guard time: 12*1955+(lo last bit)
* data: 29 bit
*
* IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC
*
* I: identification;
* Z: temperature sign;
* T: temperature sign dependent +12 Ѱ;
* B: battery low; flag to indicate low battery voltage;
* C: CRC4 (polynomial = 0x9, start_data = 0xD);
* u: unknown;
*/
static const SubGhzBlockConst ws_protocol_wendox_w6726_const = {
.te_short = 1955,
.te_long = 5865,
.te_delta = 300,
.min_count_bit_for_found = 29,
};
struct WSProtocolDecoderWendoxW6726 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
uint16_t header_count;
};
struct WSProtocolEncoderWendoxW6726 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
typedef enum {
WendoxW6726DecoderStepReset = 0,
WendoxW6726DecoderStepCheckPreambule,
WendoxW6726DecoderStepSaveDuration,
WendoxW6726DecoderStepCheckDuration,
} WendoxW6726DecoderStep;
const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = {
.alloc = ws_protocol_decoder_wendox_w6726_alloc,
.free = ws_protocol_decoder_wendox_w6726_free,
.feed = ws_protocol_decoder_wendox_w6726_feed,
.reset = ws_protocol_decoder_wendox_w6726_reset,
.get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data,
.serialize = ws_protocol_decoder_wendox_w6726_serialize,
.deserialize = ws_protocol_decoder_wendox_w6726_deserialize,
.get_string = ws_protocol_decoder_wendox_w6726_get_string,
};
const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_wendox_w6726 = {
.name = WS_PROTOCOL_WENDOX_W6726_NAME,
.type = SubGhzProtocolWeatherStation,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
.decoder = &ws_protocol_wendox_w6726_decoder,
.encoder = &ws_protocol_wendox_w6726_encoder,
};
void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726));
instance->base.protocol = &ws_protocol_wendox_w6726;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_wendox_w6726_free(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
free(instance);
}
void ws_protocol_decoder_wendox_w6726_reset(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) {
if(!instance->decoder.decode_data) return false;
uint8_t msg[] = {
instance->decoder.decode_data >> 28,
instance->decoder.decode_data >> 20,
instance->decoder.decode_data >> 12,
instance->decoder.decode_data >> 4};
uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD);
return (crc == (instance->decoder.decode_data & 0x0F));
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) {
instance->id = (instance->data >> 24) & 0xFF;
instance->battery_low = (instance->data >> 6) & 1;
instance->channel = WS_NO_CHANNEL;
if(((instance->data >> 23) & 1)) {
instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f;
} else {
instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f;
}
if(instance->temp < -50.0f) {
instance->temp = -50.0f;
} else if(instance->temp > 70.0f) {
instance->temp = 70.0f;
}
instance->btn = WS_NO_BTN;
instance->humidity = WS_NO_HUMIDITY;
}
void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
switch(instance->decoder.parser_step) {
case WendoxW6726DecoderStepReset:
if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule;
instance->decoder.te_last = duration;
instance->header_count = 0;
}
break;
case WendoxW6726DecoderStepCheckPreambule:
if(level) {
instance->decoder.te_last = duration;
} else {
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta * 1) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2)) {
instance->header_count++;
} else if((instance->header_count > 4) && (instance->header_count < 12)) {
if((DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
}
break;
case WendoxW6726DecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
break;
case WendoxW6726DecoderStepCheckDuration:
if(!level) {
if(duration >
ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) {
if(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else if(
DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
if((instance->decoder.decode_count_bit ==
ws_protocol_wendox_w6726_const.min_count_bit_for_found) &&
ws_protocol_wendox_w6726_check(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_wendox_w6726_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
break;
}
}
uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return ws_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
ws_protocol_wendox_w6726_const.min_count_bit_for_found);
}
void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%lX Ch:%d Bat:%d\r\n"
"Temp:%3.1f C Hum:%d%%",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data),
instance->generic.id,
instance->generic.channel,
instance->generic.battery_low,
(double)instance->generic.temp,
instance->generic.humidity);
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726"
typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726;
typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726;
extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder;
extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder;
extern const SubGhzProtocol ws_protocol_wendox_w6726;
/**
* Allocate WSProtocolDecoderWendoxW6726.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void ws_protocol_decoder_wendox_w6726_free(void* context);
/**
* Reset decoder WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void ws_protocol_decoder_wendox_w6726_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @return hash Hash sum
*/
uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param output Resulting text
*/
void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output);

View File

@@ -8,5 +8,5 @@ App(
order=90, order=90,
fap_icon="wifi_10px.png", fap_icon="wifi_10px.png",
fap_category="WiFi", fap_category="WiFi",
fap_libs=["assets"], fap_icon_assets="assets",
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,46 @@
#include "sequential_file.h"
char* sequential_file_resolve_path(
Storage* storage,
const char* dir,
const char* prefix,
const char* extension) {
if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) {
return NULL;
}
char file_path[256];
int file_index = 0;
do {
if(snprintf(
file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) <
0) {
return NULL;
}
file_index++;
} while(storage_file_exists(storage, file_path));
return strdup(file_path);
}
bool sequential_file_open(
Storage* storage,
File* file,
const char* dir,
const char* prefix,
const char* extension) {
if(storage == NULL || file == NULL || dir == NULL || prefix == NULL || extension == NULL) {
return false;
}
char* file_path = sequential_file_resolve_path(storage, dir, prefix, extension);
if(file_path == NULL) {
return false;
}
bool success = storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS);
free(file_path);
return success;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <storage/storage.h>
char* sequential_file_resolve_path(
Storage* storage,
const char* dir,
const char* prefix,
const char* extension);
bool sequential_file_open(
Storage* storage,
File* file,
const char* dir,
const char* prefix,
const char* extension);

View File

@@ -3,3 +3,12 @@ ADD_SCENE(wifi_marauder, console_output, ConsoleOutput)
ADD_SCENE(wifi_marauder, text_input, TextInput) ADD_SCENE(wifi_marauder, text_input, TextInput)
ADD_SCENE(wifi_marauder, settings_init, SettingsInit) ADD_SCENE(wifi_marauder, settings_init, SettingsInit)
ADD_SCENE(wifi_marauder, log_viewer, LogViewer) ADD_SCENE(wifi_marauder, log_viewer, LogViewer)
ADD_SCENE(wifi_marauder, user_input, UserInput)
ADD_SCENE(wifi_marauder, script_select, ScriptSelect)
ADD_SCENE(wifi_marauder, script_options, ScriptOptions)
ADD_SCENE(wifi_marauder, script_edit, ScriptEdit)
ADD_SCENE(wifi_marauder, script_settings, ScriptSettings)
ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete)
ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit)
ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd)
ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList)

View File

@@ -1,5 +1,34 @@
#include "../wifi_marauder_app_i.h" #include "../wifi_marauder_app_i.h"
char* _wifi_marauder_get_prefix_from_cmd(const char* command) {
int end = strcspn(command, " ");
char* prefix = (char*)malloc(sizeof(char) * (end + 1));
strncpy(prefix, command, end);
prefix[end] = '\0';
return prefix;
}
bool _wifi_marauder_is_save_pcaps_enabled(WifiMarauderApp* app) {
if(!app->ok_to_save_pcaps) {
return false;
}
// If it is a script that contains a sniff function
if(app->script != NULL) {
return wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffRaw) ||
wifi_marauder_script_has_stage(
app->script, WifiMarauderScriptStageTypeSniffBeacon) ||
wifi_marauder_script_has_stage(
app->script, WifiMarauderScriptStageTypeSniffDeauth) ||
wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffEsp) ||
wifi_marauder_script_has_stage(
app->script, WifiMarauderScriptStageTypeSniffPmkid) ||
wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffPwn);
}
// If it is a sniff function
return app->is_command && app->selected_tx_string &&
strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0;
}
void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
furi_assert(context); furi_assert(context);
WifiMarauderApp* app = context; WifiMarauderApp* app = context;
@@ -34,23 +63,29 @@ void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len,
void wifi_marauder_scene_console_output_on_enter(void* context) { void wifi_marauder_scene_console_output_on_enter(void* context) {
WifiMarauderApp* app = context; WifiMarauderApp* app = context;
// Reset text box and set font
TextBox* text_box = app->text_box; TextBox* text_box = app->text_box;
text_box_reset(app->text_box); text_box_reset(text_box);
text_box_set_font(text_box, TextBoxFontText); text_box_set_font(text_box, TextBoxFontText);
// Set focus on start or end
if(app->focus_console_start) { if(app->focus_console_start) {
text_box_set_focus(text_box, TextBoxFocusStart); text_box_set_focus(text_box, TextBoxFocusStart);
} else { } else {
text_box_set_focus(text_box, TextBoxFocusEnd); text_box_set_focus(text_box, TextBoxFocusEnd);
} }
// Set command-related messages
if(app->is_command) { if(app->is_command) {
furi_string_reset(app->text_box_store); furi_string_reset(app->text_box_store);
app->text_box_store_strlen = 0; app->text_box_store_strlen = 0;
// Help message
if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) {
const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n"; const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n";
furi_string_cat_str(app->text_box_store, help_msg); furi_string_cat_str(app->text_box_store, help_msg);
app->text_box_store_strlen += strlen(help_msg); app->text_box_store_strlen += strlen(help_msg);
} }
// Stopscan message
if(app->show_stopscan_tip) { if(app->show_stopscan_tip) {
const char* help_msg = "Press BACK to send stopscan\n"; const char* help_msg = "Press BACK to send stopscan\n";
furi_string_cat_str(app->text_box_store, help_msg); furi_string_cat_str(app->text_box_store, help_msg);
@@ -58,13 +93,14 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
} }
} }
// Set starting text - for "View Log from end", this will just be what was already in the text box store // Set starting text
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
// Set scene state and switch view
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0); scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0);
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput);
// Register callback to receive data // Register callbacks to receive data
wifi_marauder_uart_set_handle_rx_data_cb( wifi_marauder_uart_set_handle_rx_data_cb(
app->uart, app->uart,
wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread
@@ -73,25 +109,53 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread
// Get ready to send command // Get ready to send command
if(app->is_command && app->selected_tx_string) { if((app->is_command && app->selected_tx_string) || app->script) {
const char* prefix =
strlen(app->selected_tx_string) > 0 ?
_wifi_marauder_get_prefix_from_cmd(app->selected_tx_string) : // Function name
app->script->name; // Script name
// Create files *before* sending command // Create files *before* sending command
// (it takes time to iterate through the directory) // (it takes time to iterate through the directory)
if(app->ok_to_save_logs) { if(app->ok_to_save_logs) {
app->is_writing_log = true; strcpy(
wifi_marauder_create_log_file(app); app->log_file_path,
sequential_file_resolve_path(
app->storage, MARAUDER_APP_FOLDER_LOGS, prefix, "log"));
if(app->log_file_path != NULL) {
if(storage_file_open(
app->log_file, app->log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
app->is_writing_log = true;
} else {
dialog_message_show_storage_error(app->dialogs, "Cannot open log file");
}
} else {
dialog_message_show_storage_error(app->dialogs, "Cannot resolve log path");
}
} }
// If it is a sniff function, open the pcap file for recording // If it is a sniff function or script, open the pcap file for recording
if(app->ok_to_save_pcaps && if(_wifi_marauder_is_save_pcaps_enabled(app)) {
strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0) { if(sequential_file_open(
app->is_writing_pcap = true; app->storage, app->capture_file, MARAUDER_APP_FOLDER_PCAPS, prefix, "pcap")) {
wifi_marauder_create_pcap_file(app); app->is_writing_pcap = true;
} else {
dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file");
}
} }
// Send command with newline '\n' // Send command with newline '\n'
wifi_marauder_uart_tx( if(app->selected_tx_string) {
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); wifi_marauder_uart_tx(
wifi_marauder_uart_tx((uint8_t*)("\n"), 1); (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
wifi_marauder_uart_tx((uint8_t*)("\n"), 1);
}
// Run the script if the file with the script has been opened
if(app->script != NULL) {
app->script_worker = wifi_marauder_script_worker_alloc();
wifi_marauder_script_worker_start(app->script_worker, app->script);
}
} }
} }
@@ -113,14 +177,18 @@ bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEven
void wifi_marauder_scene_console_output_on_exit(void* context) { void wifi_marauder_scene_console_output_on_exit(void* context) {
WifiMarauderApp* app = context; WifiMarauderApp* app = context;
// Automatically stop the scan when exiting view
if(app->is_command) {
wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
furi_delay_ms(50);
}
// Unregister rx callback // Unregister rx callback
wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL);
wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL);
// Automatically stop the scan when exiting view wifi_marauder_script_worker_free(app->script_worker);
if(app->is_command) { app->script_worker = NULL;
wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
}
app->is_writing_pcap = false; app->is_writing_pcap = false;
if(app->capture_file && storage_file_is_open(app->capture_file)) { if(app->capture_file && storage_file_is_open(app->capture_file)) {

View File

@@ -148,8 +148,10 @@ bool wifi_marauder_scene_log_viewer_on_event(void* context, SceneManagerEvent ev
// Browse // Browse
FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS); FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS);
FuriString* selected_filepath = furi_string_alloc(); FuriString* selected_filepath = furi_string_alloc();
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, ".log", &I_Text_10x10);
if(dialog_file_browser_show( if(dialog_file_browser_show(
app->dialogs, selected_filepath, predefined_filepath, NULL)) { app->dialogs, selected_filepath, predefined_filepath, &browser_options)) {
strncpy( strncpy(
app->log_file_path, app->log_file_path,
furi_string_get_cstr(selected_filepath), furi_string_get_cstr(selected_filepath),

View File

@@ -0,0 +1,83 @@
#include "../wifi_marauder_app_i.h"
void wifi_marauder_scene_script_confirm_delete_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
WifiMarauderApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void wifi_marauder_scene_script_confirm_delete_on_enter(void* context) {
WifiMarauderApp* app = context;
widget_add_button_element(
app->widget,
GuiButtonTypeLeft,
"No",
wifi_marauder_scene_script_confirm_delete_widget_callback,
app);
widget_add_button_element(
app->widget,
GuiButtonTypeRight,
"Yes",
wifi_marauder_scene_script_confirm_delete_widget_callback,
app);
widget_add_string_element(
app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Are you sure?");
widget_add_text_box_element(
app->widget,
0,
12,
128,
38,
AlignCenter,
AlignCenter,
"The script will be\npermanently deleted",
false);
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget);
}
bool wifi_marauder_scene_script_confirm_delete_on_event(void* context, SceneManagerEvent event) {
WifiMarauderApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
// get which button press: "Yes" or "No"
if(event.event == GuiButtonTypeRight) {
// Yes
if(app->script != NULL) {
char script_path[256];
snprintf(
script_path,
sizeof(script_path),
"%s/%s.json",
MARAUDER_APP_FOLDER_SCRIPTS,
app->script->name);
storage_simply_remove(app->storage, script_path);
wifi_marauder_script_free(app->script);
app->script = NULL;
DialogMessage* message = dialog_message_alloc();
dialog_message_set_text(message, "Deleted!", 88, 32, AlignCenter, AlignCenter);
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6);
dialog_message_set_buttons(message, NULL, "Ok", NULL);
dialog_message_show(app->dialogs, message);
dialog_message_free(message);
}
}
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
return consumed;
}
void wifi_marauder_scene_script_confirm_delete_on_exit(void* context) {
WifiMarauderApp* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,125 @@
#include "../wifi_marauder_app_i.h"
static void wifi_marauder_scene_script_edit_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
WifiMarauderScriptStage* current_stage = app->script->first_stage;
uint32_t stage_index = 0;
while(current_stage != NULL && stage_index < index) {
current_stage = current_stage->next_stage;
stage_index++;
}
app->script_edit_selected_stage = current_stage;
if(app->script_edit_selected_stage != NULL) {
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEdit);
}
}
static void wifi_marauder_scene_script_edit_add_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageAdd);
}
void wifi_marauder_scene_script_edit_on_enter(void* context) {
WifiMarauderApp* app = context;
Submenu* submenu = app->submenu;
WifiMarauderScript* script = app->script;
submenu_set_header(submenu, script->name);
WifiMarauderScriptStage* current_stage = script->first_stage;
int stage_index = 0;
while(current_stage != NULL) {
switch(current_stage->type) {
case WifiMarauderScriptStageTypeScan:
submenu_add_item(
submenu, "Scan", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeSelect:
submenu_add_item(
submenu, "Select", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeDeauth:
submenu_add_item(
submenu, "Deauth", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeProbe:
submenu_add_item(
submenu, "Probe", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeSniffRaw:
submenu_add_item(
submenu, "Sniff raw", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeSniffBeacon:
submenu_add_item(
submenu,
"Sniff beacon",
stage_index,
wifi_marauder_scene_script_edit_callback,
app);
break;
case WifiMarauderScriptStageTypeSniffDeauth:
submenu_add_item(
submenu,
"Sniff deauth",
stage_index,
wifi_marauder_scene_script_edit_callback,
app);
break;
case WifiMarauderScriptStageTypeSniffEsp:
submenu_add_item(
submenu, "Sniff esp", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeSniffPmkid:
submenu_add_item(
submenu, "Sniff PMKID", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeSniffPwn:
submenu_add_item(
submenu, "Sniff pwn", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeBeaconList:
submenu_add_item(
submenu, "Beacon list", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeBeaconAp:
submenu_add_item(
submenu, "Beacon AP", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
case WifiMarauderScriptStageTypeExec:
submenu_add_item(
submenu,
"Custom command",
stage_index,
wifi_marauder_scene_script_edit_callback,
app);
break;
case WifiMarauderScriptStageTypeDelay:
submenu_add_item(
submenu, "Delay", stage_index, wifi_marauder_scene_script_edit_callback, app);
break;
}
current_stage = current_stage->next_stage;
stage_index++;
}
submenu_add_item(
submenu, "[+] ADD STAGE", stage_index++, wifi_marauder_scene_script_edit_add_callback, app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu);
}
bool wifi_marauder_scene_script_edit_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_script_edit_on_exit(void* context) {
WifiMarauderApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,188 @@
#include "../wifi_marauder_app_i.h"
static void
wifi_marauder_scene_script_stage_edit_list_add_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
// Creates new item
WifiMarauderScriptStageListItem* new_item =
(WifiMarauderScriptStageListItem*)malloc(sizeof(WifiMarauderScriptStageListItem));
new_item->value = malloc(64);
new_item->next_item = NULL;
if(app->script_stage_edit_first_item == NULL) {
app->script_stage_edit_first_item = new_item;
} else {
WifiMarauderScriptStageListItem* last_item = app->script_stage_edit_first_item;
while(last_item->next_item != NULL) {
last_item = last_item->next_item;
}
last_item->next_item = new_item;
}
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList, index);
app->user_input_type = WifiMarauderUserInputTypeString;
app->user_input_string_reference = &new_item->value;
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
}
static void wifi_marauder_scene_script_stage_edit_list_deallocate_items(WifiMarauderApp* app) {
WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
while(current_item != NULL) {
WifiMarauderScriptStageListItem* next_item = current_item->next_item;
free(current_item->value);
free(current_item);
current_item = next_item;
}
app->script_stage_edit_first_item = NULL;
}
static void wifi_marauder_scene_script_stage_edit_list_save_strings(WifiMarauderApp* app) {
WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
int array_size = 0;
// Calculates the required array size
while(current_item != NULL) {
array_size++;
current_item = current_item->next_item;
}
// Reallocate the array of strings if necessary
if(*app->script_stage_edit_string_count_reference < array_size) {
*app->script_stage_edit_strings_reference =
realloc(*app->script_stage_edit_strings_reference, array_size * sizeof(char*));
}
// Fills the array of strings
current_item = app->script_stage_edit_first_item;
int i = 0;
while(current_item != NULL) {
char* current_str = malloc(strlen(current_item->value) + 1);
strncpy(current_str, current_item->value, strlen(current_item->value) + 1);
(*app->script_stage_edit_strings_reference)[i] = current_str;
current_item = current_item->next_item;
i++;
}
*app->script_stage_edit_string_count_reference = array_size;
}
static void wifi_marauder_scene_script_stage_edit_list_save_numbers(WifiMarauderApp* app) {
WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
int array_size = 0;
// Calculates the required array size
while(current_item != NULL) {
array_size++;
current_item = current_item->next_item;
}
// Reallocate the array of integers if necessary
if(*app->script_stage_edit_number_count_reference < array_size) {
*app->script_stage_edit_numbers_reference =
realloc(*app->script_stage_edit_numbers_reference, array_size * sizeof(int));
}
// Fills the array of integers
current_item = app->script_stage_edit_first_item;
int i = 0;
while(current_item != NULL) {
(*app->script_stage_edit_numbers_reference)[i] = atoi(current_item->value);
current_item = current_item->next_item;
i++;
}
*app->script_stage_edit_number_count_reference = array_size;
}
static void
wifi_marauder_scene_script_stage_edit_list_save_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
if(app->script_stage_edit_strings_reference != NULL &&
app->script_stage_edit_string_count_reference != NULL) {
wifi_marauder_scene_script_stage_edit_list_save_strings(app);
}
if(app->script_stage_edit_numbers_reference != NULL &&
app->script_stage_edit_number_count_reference != NULL) {
wifi_marauder_scene_script_stage_edit_list_save_numbers(app);
}
wifi_marauder_scene_script_stage_edit_list_deallocate_items(app);
scene_manager_previous_scene(app->scene_manager);
}
static void
wifi_marauder_scene_script_stage_edit_list_clear_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
wifi_marauder_scene_script_stage_edit_list_deallocate_items(app);
submenu_reset(app->submenu);
submenu_add_item(
app->submenu,
"[+] ADD ITEM",
99,
wifi_marauder_scene_script_stage_edit_list_add_callback,
app);
submenu_add_item(
app->submenu,
"[*] SAVE ITEMS",
99,
wifi_marauder_scene_script_stage_edit_list_save_callback,
app);
submenu_add_item(
app->submenu,
"[-] CLEAR LIST",
99,
wifi_marauder_scene_script_stage_edit_list_clear_callback,
app);
}
void wifi_marauder_scene_script_stage_edit_list_on_enter(void* context) {
WifiMarauderApp* app = context;
int item_index = 0;
WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
while(current_item != NULL) {
submenu_add_item(app->submenu, current_item->value, item_index++, NULL, app);
current_item = current_item->next_item;
}
submenu_add_item(
app->submenu,
"[+] ADD ITEM",
99,
wifi_marauder_scene_script_stage_edit_list_add_callback,
app);
submenu_add_item(
app->submenu,
"[*] SAVE ITEMS",
99,
wifi_marauder_scene_script_stage_edit_list_save_callback,
app);
submenu_add_item(
app->submenu,
"[-] CLEAR LIST",
99,
wifi_marauder_scene_script_stage_edit_list_clear_callback,
app);
submenu_set_selected_item(
app->submenu,
scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu);
}
bool wifi_marauder_scene_script_stage_edit_list_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_script_stage_edit_list_on_exit(void* context) {
WifiMarauderApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,111 @@
#include "../wifi_marauder_app_i.h"
enum SubmenuIndex {
SubmenuIndexRun,
SubmenuIndexSettings,
SubmenuIndexEditStages,
SubmenuIndexSave,
SubmenuIndexDelete
};
void wifi_marauder_scene_script_options_save_script(WifiMarauderApp* app) {
char script_path[256];
snprintf(
script_path,
sizeof(script_path),
"%s/%s.json",
MARAUDER_APP_FOLDER_SCRIPTS,
app->script->name);
wifi_marauder_script_save_json(app->storage, script_path, app->script);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_text(message, "Saved!", 88, 32, AlignCenter, AlignCenter);
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6);
dialog_message_set_buttons(message, NULL, "Ok", NULL);
dialog_message_show(app->dialogs, message);
dialog_message_free(message);
}
static void wifi_marauder_scene_script_options_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
switch(index) {
case SubmenuIndexRun:
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
break;
case SubmenuIndexSettings:
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSettings);
break;
case SubmenuIndexEditStages:
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptEdit);
break;
case SubmenuIndexSave:
wifi_marauder_scene_script_options_save_script(app);
break;
case SubmenuIndexDelete:
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptConfirmDelete);
break;
}
}
void wifi_marauder_scene_script_options_on_enter(void* context) {
WifiMarauderApp* app = context;
// If returning after confirming script deletion
if(app->script == NULL) {
scene_manager_previous_scene(app->scene_manager);
return;
}
Submenu* submenu = app->submenu;
submenu_set_header(submenu, app->script->name);
submenu_add_item(
submenu, "[>] RUN", SubmenuIndexRun, wifi_marauder_scene_script_options_callback, app);
submenu_add_item(
submenu,
"[S] SETTINGS",
SubmenuIndexSettings,
wifi_marauder_scene_script_options_callback,
app);
submenu_add_item(
submenu,
"[+] EDIT STAGES",
SubmenuIndexEditStages,
wifi_marauder_scene_script_options_callback,
app);
submenu_add_item(
submenu, "[*] SAVE", SubmenuIndexSave, wifi_marauder_scene_script_options_callback, app);
submenu_add_item(
submenu,
"[X] DELETE",
SubmenuIndexDelete,
wifi_marauder_scene_script_options_callback,
app);
submenu_set_selected_item(
submenu,
scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu);
}
bool wifi_marauder_scene_script_options_on_event(void* context, SceneManagerEvent event) {
WifiMarauderApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
wifi_marauder_script_free(app->script);
app->script = NULL;
}
return consumed;
}
void wifi_marauder_scene_script_options_on_exit(void* context) {
WifiMarauderApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,90 @@
#include "../wifi_marauder_app_i.h"
static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
char script_path[256];
snprintf(
script_path,
sizeof(script_path),
"%s/%s.json",
MARAUDER_APP_FOLDER_SCRIPTS,
furi_string_get_cstr(app->script_list[index]));
app->script = wifi_marauder_script_parse_json(app->storage, script_path);
if(app->script) {
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptOptions);
}
}
static void wifi_marauder_scene_script_select_add_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index);
app->user_input_type = WifiMarauderUserInputTypeFileName;
app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER_SCRIPTS);
app->user_input_file_extension = strdup("json");
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
}
void wifi_marauder_scene_script_select_on_enter(void* context) {
WifiMarauderApp* app = context;
Submenu* submenu = app->submenu;
File* dir_scripts = storage_file_alloc(app->storage);
if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) {
FileInfo file_info;
char file_path[255];
app->script_list_count = 0;
// Goes through the files in the folder counting the ones that end with the json extension
while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) {
app->script_list_count++;
}
if(app->script_list_count > 0) {
submenu_set_header(submenu, "Select a script:");
app->script_list = malloc(app->script_list_count * sizeof(FuriString*));
storage_dir_close(dir_scripts);
storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS);
// Read the files again from the beginning, adding the scripts in the list
int script_index = 0;
while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) {
app->script_list[script_index] = furi_string_alloc();
path_extract_filename_no_ext(file_path, app->script_list[script_index]);
submenu_add_item(
submenu,
furi_string_get_cstr(app->script_list[script_index]),
script_index,
wifi_marauder_scene_script_select_callback,
app);
script_index++;
}
} else {
submenu_set_header(submenu, "No script found");
}
submenu_add_item(
submenu, "[+] ADD SCRIPT", 99, wifi_marauder_scene_script_select_add_callback, app);
storage_dir_close(dir_scripts);
}
storage_file_free(dir_scripts);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu);
}
bool wifi_marauder_scene_script_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_script_select_on_exit(void* context) {
WifiMarauderApp* app = context;
submenu_reset(app->submenu);
for(int i = 0; i < app->script_list_count; i++) {
furi_string_free(app->script_list[i]);
}
free(app->script_list);
}

View File

@@ -0,0 +1,87 @@
#include "../wifi_marauder_app_i.h"
enum ScriptSettingsOption {
ScriptSettingsOptionRepeat,
ScriptSettingsOptionSavePcap,
ScriptSettingsOptionEnableLed
};
const char* option_values[3] = {"No", "Yes", "Default"};
static void wifi_marauder_scene_script_settings_enter_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
// Accept script repeat value
if(index == ScriptSettingsOptionRepeat) {
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings, index);
app->user_input_type = WifiMarauderUserInputTypeNumber;
app->user_input_number_reference = &app->script->repeat;
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
}
}
static void wifi_marauder_scene_script_settings_change_callback(VariableItem* item) {
WifiMarauderApp* app = variable_item_get_context(item);
uint8_t current_option = variable_item_list_get_selected_item_index(app->var_item_list);
uint8_t option_value_index = variable_item_get_current_value_index(item);
switch(current_option) {
case ScriptSettingsOptionSavePcap:
variable_item_set_current_value_text(item, option_values[option_value_index]);
app->script->save_pcap = option_value_index;
break;
case ScriptSettingsOptionEnableLed:
variable_item_set_current_value_text(item, option_values[option_value_index]);
app->script->enable_led = option_value_index;
break;
}
}
void wifi_marauder_scene_script_settings_on_enter(void* context) {
WifiMarauderApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
variable_item_list_set_enter_callback(
app->var_item_list, wifi_marauder_scene_script_settings_enter_callback, app);
// Script repeat option
VariableItem* repeat_item = variable_item_list_add(app->var_item_list, "Repeat", 1, NULL, app);
char repeat_str[32];
snprintf(repeat_str, sizeof(repeat_str), "%d", app->script->repeat);
variable_item_set_current_value_text(repeat_item, repeat_str);
// Save PCAP option
VariableItem* save_pcap_item = variable_item_list_add(
app->var_item_list,
"Save PCAP",
3,
wifi_marauder_scene_script_settings_change_callback,
app);
variable_item_set_current_value_index(save_pcap_item, app->script->save_pcap);
variable_item_set_current_value_text(save_pcap_item, option_values[app->script->save_pcap]);
// Enable board LED option
VariableItem* enable_led_item = variable_item_list_add(
app->var_item_list,
"Enable LED",
3,
wifi_marauder_scene_script_settings_change_callback,
app);
variable_item_set_current_value_index(enable_led_item, app->script->enable_led);
variable_item_set_current_value_text(enable_led_item, option_values[app->script->enable_led]);
variable_item_list_set_selected_item(
var_item_list,
scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList);
}
bool wifi_marauder_scene_script_settings_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_script_settings_on_exit(void* context) {
WifiMarauderApp* app = context;
variable_item_list_reset(app->var_item_list);
}

View File

@@ -0,0 +1,297 @@
#include "../wifi_marauder_app_i.h"
// Scan
static void wifi_marauder_scene_script_stage_add_scan_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageScan* stage =
(WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan));
stage->type = WifiMarauderScriptScanTypeAp;
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeScan, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Select
static void wifi_marauder_scene_script_stage_add_select_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSelect* stage =
(WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect));
stage->type = WifiMarauderScriptSelectTypeAp;
stage->filter = strdup("all");
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSelect, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Deauth
static void wifi_marauder_scene_script_stage_add_deauth_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageDeauth* stage =
(WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDeauth, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Probe
static void wifi_marauder_scene_script_stage_add_probe_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageProbe* stage =
(WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeProbe, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Sniff RAW
static void wifi_marauder_scene_script_stage_add_sniffraw_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSniffRaw* stage =
(WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffRaw, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Sniff Beacon
static void
wifi_marauder_scene_script_stage_add_sniffbeacon_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSniffBeacon* stage =
(WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffBeacon, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Sniff Deauth
static void
wifi_marauder_scene_script_stage_add_sniffdeauth_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSniffDeauth* stage =
(WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffDeauth, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Sniff Esp
static void wifi_marauder_scene_script_stage_add_sniffesp_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSniffEsp* stage =
(WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffEsp, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Sniff PMKID
static void
wifi_marauder_scene_script_stage_add_sniffpmkid_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSniffPmkid* stage =
(WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid));
stage->channel = 0;
stage->force_deauth = WifiMarauderScriptBooleanTrue;
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPmkid, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Sniff Pwn
static void wifi_marauder_scene_script_stage_add_sniffpwn_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageSniffPwn* stage =
(WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPwn, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Beacon list
static void
wifi_marauder_scene_script_stage_add_beaconlist_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageBeaconList* stage =
(WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList));
stage->ssids = NULL;
stage->ssid_count = 0;
stage->random_ssids = 0;
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconList, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Beacon AP
static void wifi_marauder_scene_script_stage_add_beaconap_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageBeaconAp* stage =
(WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp));
stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconAp, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Exec
static void wifi_marauder_scene_script_stage_add_exec_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageExec* stage =
(WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec));
stage->command = NULL;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeExec, stage);
scene_manager_previous_scene(app->scene_manager);
}
// Delay
static void wifi_marauder_scene_script_stage_add_delay_callback(void* context, uint32_t index) {
UNUSED(index);
WifiMarauderApp* app = context;
WifiMarauderScriptStageDelay* stage =
(WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay));
stage->timeout = 0;
wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDelay, stage);
scene_manager_previous_scene(app->scene_manager);
}
void wifi_marauder_scene_script_stage_add_on_enter(void* context) {
WifiMarauderApp* app = context;
Submenu* submenu = app->submenu;
submenu_set_header(submenu, "Add stage");
int menu_index = 0;
submenu_add_item(
submenu, "[+] Scan", menu_index++, wifi_marauder_scene_script_stage_add_scan_callback, app);
submenu_add_item(
submenu,
"[+] Select",
menu_index++,
wifi_marauder_scene_script_stage_add_select_callback,
app);
submenu_add_item(
submenu,
"[+] Deauth",
menu_index++,
wifi_marauder_scene_script_stage_add_deauth_callback,
app);
submenu_add_item(
submenu,
"[+] Probe",
menu_index++,
wifi_marauder_scene_script_stage_add_probe_callback,
app);
submenu_add_item(
submenu,
"[+] Sniff RAW",
menu_index++,
wifi_marauder_scene_script_stage_add_sniffraw_callback,
app);
submenu_add_item(
submenu,
"[+] Sniff Beacon",
menu_index++,
wifi_marauder_scene_script_stage_add_sniffbeacon_callback,
app);
submenu_add_item(
submenu,
"[+] Sniff Deauth",
menu_index++,
wifi_marauder_scene_script_stage_add_sniffdeauth_callback,
app);
submenu_add_item(
submenu,
"[+] Sniff Esp",
menu_index++,
wifi_marauder_scene_script_stage_add_sniffesp_callback,
app);
submenu_add_item(
submenu,
"[+] Sniff PMKID",
menu_index++,
wifi_marauder_scene_script_stage_add_sniffpmkid_callback,
app);
submenu_add_item(
submenu,
"[+] Sniff Pwnagotchi",
menu_index++,
wifi_marauder_scene_script_stage_add_sniffpwn_callback,
app);
submenu_add_item(
submenu,
"[+] Beacon List",
menu_index++,
wifi_marauder_scene_script_stage_add_beaconlist_callback,
app);
submenu_add_item(
submenu,
"[+] Beacon AP",
menu_index++,
wifi_marauder_scene_script_stage_add_beaconap_callback,
app);
submenu_add_item(
submenu,
"[+] Custom command",
menu_index++,
wifi_marauder_scene_script_stage_add_exec_callback,
app);
submenu_add_item(
submenu,
"[+] Delay",
menu_index++,
wifi_marauder_scene_script_stage_add_delay_callback,
app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu);
}
bool wifi_marauder_scene_script_stage_add_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_script_stage_add_on_exit(void* context) {
WifiMarauderApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,203 @@
#include "../wifi_marauder_app_i.h"
void wifi_marauder_scene_script_stage_edit_create_list_strings(
WifiMarauderApp* app,
char** strings,
int string_count) {
// Deallocates the existing list
WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
while(current_item != NULL) {
WifiMarauderScriptStageListItem* next_item = current_item->next_item;
free(current_item->value);
free(current_item);
current_item = next_item;
}
// Create a new list with numbers
WifiMarauderScriptStageListItem* first_item = NULL;
WifiMarauderScriptStageListItem* previous_item = NULL;
for(int i = 0; i < string_count; i++) {
WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem));
item->value = strdup(strings[i]);
item->next_item = NULL;
if(previous_item == NULL) {
first_item = item;
} else {
previous_item->next_item = item;
}
previous_item = item;
}
app->script_stage_edit_first_item = first_item;
}
void wifi_marauder_scene_script_stage_edit_create_list_numbers(
WifiMarauderApp* app,
int* numbers,
int number_count) {
// Deallocates the existing list
WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
while(current_item != NULL) {
WifiMarauderScriptStageListItem* next_item = current_item->next_item;
free(current_item->value);
free(current_item);
current_item = next_item;
}
// Create a new list with numbers
WifiMarauderScriptStageListItem* first_item = NULL;
WifiMarauderScriptStageListItem* previous_item = NULL;
for(int i = 0; i < number_count; i++) {
char number_str[32];
snprintf(number_str, sizeof(number_str), "%d", numbers[i]);
WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem));
item->value = strdup(number_str);
item->next_item = NULL;
if(previous_item == NULL) {
first_item = item;
} else {
previous_item->next_item = item;
}
previous_item = item;
}
app->script_stage_edit_first_item = first_item;
}
static void
wifi_marauder_scene_script_stage_edit_list_enter_callback(void* context, uint32_t index) {
WifiMarauderApp* app = context;
const WifiMarauderScriptMenuItem* menu_item = &app->script_stage_menu->items[index];
// Fixed delete item
if(index == app->script_stage_menu->num_items) {
uint32_t deleted_stage_index =
scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit);
if(deleted_stage_index > 0) {
scene_manager_set_scene_state(
app->scene_manager, WifiMarauderSceneScriptEdit, deleted_stage_index - 1);
}
WifiMarauderScriptStage* previous_stage = NULL;
WifiMarauderScriptStage* current_stage = app->script->first_stage;
uint32_t current_stage_index = 0;
while(current_stage != NULL && current_stage_index < deleted_stage_index) {
previous_stage = current_stage;
current_stage = current_stage->next_stage;
current_stage_index++;
}
// Delete the stage
if(current_stage != NULL) {
if(previous_stage != NULL) {
if(current_stage->next_stage != NULL) {
previous_stage->next_stage = current_stage->next_stage;
} else {
previous_stage->next_stage = NULL;
app->script->last_stage = previous_stage;
}
} else {
if(current_stage->next_stage != NULL) {
app->script->first_stage = current_stage->next_stage;
} else {
app->script->first_stage = NULL;
app->script->last_stage = NULL;
}
}
}
app->script_edit_selected_stage = NULL;
scene_manager_previous_scene(app->scene_manager);
return;
}
if(menu_item->select_callback == NULL) {
return;
}
if(menu_item->type == WifiMarauderScriptMenuItemTypeNumber) {
// Accepts user number input, assigning the value to the reference passed as a parameter
menu_item->select_callback(app);
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
app->user_input_type = WifiMarauderUserInputTypeNumber;
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
} else if(menu_item->type == WifiMarauderScriptMenuItemTypeString) {
// Accepts user string input, assigning the value to the reference passed as a parameter
menu_item->select_callback(app);
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
app->user_input_type = WifiMarauderUserInputTypeString;
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
} else if(menu_item->type == WifiMarauderScriptMenuItemTypeListString) {
// Accepts the strings that compose the list
menu_item->select_callback(app);
wifi_marauder_scene_script_stage_edit_create_list_strings(
app,
*app->script_stage_edit_strings_reference,
*app->script_stage_edit_string_count_reference);
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList);
} else if(menu_item->type == WifiMarauderScriptMenuItemTypeListNumber) {
// Accepts the numbers that compose the list
menu_item->select_callback(app);
wifi_marauder_scene_script_stage_edit_create_list_numbers(
app,
*app->script_stage_edit_numbers_reference,
*app->script_stage_edit_number_count_reference);
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList);
}
}
void wifi_marauder_scene_script_stage_edit_on_enter(void* context) {
WifiMarauderApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
variable_item_list_set_enter_callback(
app->var_item_list, wifi_marauder_scene_script_stage_edit_list_enter_callback, app);
app->script_stage_menu =
wifi_marauder_script_stage_menu_create(app->script_edit_selected_stage->type);
if(app->script_stage_menu->items != NULL) {
for(uint32_t i = 0; i < app->script_stage_menu->num_items; i++) {
WifiMarauderScriptMenuItem* stage_item = &app->script_stage_menu->items[i];
// Changes the list item to handle it in callbacks
VariableItem* list_item = variable_item_list_add(
app->var_item_list,
stage_item->name,
stage_item->num_options,
stage_item->change_callback,
app);
variable_item_list_set_selected_item(app->var_item_list, i);
if(stage_item->setup_callback != NULL) {
stage_item->setup_callback(list_item);
}
if(stage_item->change_callback != NULL) {
stage_item->change_callback(list_item);
}
}
}
variable_item_list_add(app->var_item_list, "[-] DELETE STAGE", 0, NULL, app);
variable_item_list_set_selected_item(
var_item_list,
scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList);
}
bool wifi_marauder_scene_script_stage_edit_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_script_stage_edit_on_exit(void* context) {
WifiMarauderApp* app = context;
wifi_marauder_script_stage_menu_free(app->script_stage_menu);
app->script_stage_menu = NULL;
variable_item_list_reset(app->var_item_list);
}

View File

@@ -127,6 +127,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = {
{"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
{"Scripts", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Save to flipper sdcard", // keep as last entry or change logic in callback below {"Save to flipper sdcard", // keep as last entry or change logic in callback below
{""}, {""},
1, 1,
@@ -143,13 +144,6 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin
furi_assert(index < NUM_MENU_ITEMS); furi_assert(index < NUM_MENU_ITEMS);
const WifiMarauderItem* item = &items[index]; const WifiMarauderItem* item = &items[index];
if(index == NUM_MENU_ITEMS - 1) {
// "Save to flipper sdcard" special case - start SettingsInit widget
view_dispatcher_send_custom_event(
app->view_dispatcher, WifiMarauderEventStartSettingsInit);
return;
}
const int selected_option_index = app->selected_option_index[index]; const int selected_option_index = app->selected_option_index[index];
furi_assert(selected_option_index < item->num_options_menu); furi_assert(selected_option_index < item->num_options_menu);
app->selected_tx_string = item->actual_commands[selected_option_index]; app->selected_tx_string = item->actual_commands[selected_option_index];
@@ -167,6 +161,20 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin
return; return;
} }
// Select automation script
if(index == NUM_MENU_ITEMS - 2) {
view_dispatcher_send_custom_event(
app->view_dispatcher, WifiMarauderEventStartScriptSelect);
return;
}
if(index == NUM_MENU_ITEMS - 1) {
// "Save to flipper sdcard" special case - start SettingsInit widget
view_dispatcher_send_custom_event(
app->view_dispatcher, WifiMarauderEventStartSettingsInit);
return;
}
bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) : bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) :
item->needs_keyboard; item->needs_keyboard;
if(needs_keyboard) { if(needs_keyboard) {
@@ -242,6 +250,10 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event)
scene_manager_set_scene_state( scene_manager_set_scene_state(
app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer);
} else if(event.event == WifiMarauderEventStartScriptSelect) {
scene_manager_set_scene_state(
app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSelect);
} }
consumed = true; consumed = true;
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {

View File

@@ -0,0 +1,155 @@
#include "../wifi_marauder_app_i.h"
bool wifi_marauder_scene_user_input_validator_number_callback(
const char* text,
FuriString* error,
void* context) {
UNUSED(context);
for(int i = 0; text[i] != '\0'; i++) {
if(text[i] < '0' || text[i] > '9') {
furi_string_printf(error, "This is not\na valid\nnumber!");
return false;
}
}
return true;
}
bool wifi_marauder_scene_user_input_validator_file_callback(
const char* text,
FuriString* error,
void* context) {
UNUSED(context);
if(strlen(text) == 0) {
furi_string_printf(error, "File name\ncannot be\nblank!");
return false;
}
return true;
}
void wifi_marauder_scene_user_input_ok_callback(void* context) {
WifiMarauderApp* app = context;
File* file = NULL;
char* file_path = NULL;
switch(app->user_input_type) {
// Writes the string value of the reference
case WifiMarauderUserInputTypeString:
if(app->user_input_string_reference != NULL) {
strncpy(
*app->user_input_string_reference,
app->text_input_store,
strlen(app->text_input_store) + 1);
app->user_input_string_reference = NULL;
}
break;
// Writes the numerical value of the reference
case WifiMarauderUserInputTypeNumber:
if(app->user_input_number_reference != NULL) {
*app->user_input_number_reference = atoi(app->text_input_store);
app->user_input_number_reference = NULL;
}
break;
// Creates a file with the name entered by the user, if it does not exist
case WifiMarauderUserInputTypeFileName:
file = storage_file_alloc(app->storage);
// Use application directory if not specified
if(app->user_input_file_dir == NULL) {
app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER);
}
if(app->user_input_file_extension != NULL) {
size_t file_path_len = strlen(app->user_input_file_dir) +
strlen(app->text_input_store) +
strlen(app->user_input_file_extension) + 3;
file_path = (char*)malloc(file_path_len);
snprintf(
file_path,
file_path_len,
"%s/%s.%s",
app->user_input_file_dir,
app->text_input_store,
app->user_input_file_extension);
} else {
size_t file_path_len =
strlen(app->user_input_file_dir) + strlen(app->text_input_store) + 2;
file_path = (char*)malloc(file_path_len);
snprintf(
file_path, file_path_len, "%s/%s", app->user_input_file_dir, app->text_input_store);
}
if(storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_NEW)) {
storage_file_close(file);
}
// Free memory
free(app->user_input_file_dir);
app->user_input_file_dir = NULL;
free(app->user_input_file_extension);
app->user_input_file_extension = NULL;
free(file_path);
storage_file_free(file);
break;
default:
break;
}
scene_manager_previous_scene(app->scene_manager);
}
void wifi_marauder_scene_user_input_on_enter(void* context) {
WifiMarauderApp* app = context;
switch(app->user_input_type) {
// Loads the string value of the reference
case WifiMarauderUserInputTypeString:
wifi_text_input_set_header_text(app->text_input, "Enter value:");
wifi_text_input_set_validator(app->text_input, NULL, app);
if(app->user_input_string_reference != NULL) {
strncpy(
app->text_input_store,
*app->user_input_string_reference,
strlen(*app->user_input_string_reference) + 1);
}
break;
// Loads the numerical value of the reference
case WifiMarauderUserInputTypeNumber:
wifi_text_input_set_header_text(app->text_input, "Enter a valid number:");
wifi_text_input_set_validator(
app->text_input, wifi_marauder_scene_user_input_validator_number_callback, app);
if(app->user_input_number_reference != NULL) {
char number_str[32];
snprintf(number_str, sizeof(number_str), "%d", *app->user_input_number_reference);
strncpy(app->text_input_store, number_str, strlen(number_str) + 1);
}
break;
// File name
case WifiMarauderUserInputTypeFileName:
wifi_text_input_set_header_text(app->text_input, "Enter file name:");
wifi_text_input_set_validator(
app->text_input, wifi_marauder_scene_user_input_validator_file_callback, app);
break;
default:
scene_manager_previous_scene(app->scene_manager);
return;
}
wifi_text_input_set_result_callback(
app->text_input,
wifi_marauder_scene_user_input_ok_callback,
app,
app->text_input_store,
WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE,
false);
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewTextInput);
}
bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void wifi_marauder_scene_user_input_on_exit(void* context) {
WifiMarauderApp* app = context;
memset(app->text_input_store, 0, sizeof(app->text_input_store));
wifi_text_input_reset(app->text_input);
}

Some files were not shown because too many files have changed in this diff Show More