Alexander Kopachov 2 лет назад
Родитель
Сommit
6ca8a7093d

+ 4 - 1
application.fam

@@ -28,7 +28,10 @@ App(
             name="base32",
             name="base32",
         ),
         ),
         Lib(
         Lib(
-            name="list",
+            name="base64",
+        ),
+        Lib(
+            name="linked_list"
         ),
         ),
         Lib(
         Lib(
             name="timezone_utils",
             name="timezone_utils",

+ 3 - 1
cli/cli_helpers.c

@@ -41,7 +41,9 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) {
         } else if(c == CliSymbolAsciiETX) {
         } else if(c == CliSymbolAsciiETX) {
             cli_nl();
             cli_nl();
             return false;
             return false;
-        } else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+        } else if(
+            (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+            c == '/' || c == '=' || c == '+') {
             if(mask_user_input) {
             if(mask_user_input) {
                 putc('*', stdout);
                 putc('*', stdout);
             } else {
             } else {

+ 7 - 18
cli/cli_helpers.h

@@ -14,24 +14,13 @@
 #define DOCOPT_OPTIONS "[options]"
 #define DOCOPT_OPTIONS "[options]"
 #define DOCOPT_DEFAULT(val) "[default: " val "]"
 #define DOCOPT_DEFAULT(val) "[default: " val "]"
 
 
-#define TOTP_CLI_PRINTF(format, ...)                                        \
-    do {                                                                    \
-        _Pragma(STRINGIFY(GCC diagnostic push))                             \
-            _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \
-                printf(format, ##__VA_ARGS__);                              \
-        _Pragma(STRINGIFY(GCC diagnostic pop))                              \
-    } while(false)
-
-#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...)                        \
-    do {                                                                    \
-        _Pragma(STRINGIFY(GCC diagnostic push))                             \
-            _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \
-                printf("\e[%s", color);                                     \
-        printf(format, ##__VA_ARGS__);                                      \
-        printf("\e[0m");                                                    \
-        fflush(stdout);                                                     \
-        _Pragma(STRINGIFY(GCC diagnostic pop))                              \
-    } while(false)
+#define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__)
+
+#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
+    printf("\e[%s", color);                          \
+    printf(format, ##__VA_ARGS__);                   \
+    printf("\e[0m");                                 \
+    fflush(stdout)
 
 
 #define TOTP_CLI_COLOR_ERROR "91m"
 #define TOTP_CLI_COLOR_ERROR "91m"
 #define TOTP_CLI_COLOR_WARNING "93m"
 #define TOTP_CLI_COLOR_WARNING "93m"

+ 37 - 21
cli/commands/add/add.c

@@ -1,7 +1,7 @@
 #include "add.h"
 #include "add.h"
 #include <stdlib.h>
 #include <stdlib.h>
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
-#include "../../../lib/list/list.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"
@@ -35,11 +35,21 @@ void totp_cli_command_add_docopt_options() {
             TOTP_CLI_COMMAND_ARG_ALGO)) "      Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME
             TOTP_CLI_COMMAND_ARG_ALGO)) "      Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME
                                         ", " TOTP_TOKEN_ALGO_SHA256_NAME
                                         ", " TOTP_TOKEN_ALGO_SHA256_NAME
                                         ", " TOTP_TOKEN_ALGO_SHA512_NAME
                                         ", " TOTP_TOKEN_ALGO_SHA512_NAME
+                                        ", " TOTP_TOKEN_ALGO_STEAM_NAME
                                         " " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n");
                                         " " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n");
     TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
     TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
         TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
         TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
         DOCOPT_ARGUMENT(
         DOCOPT_ARGUMENT(
-            TOTP_CLI_COMMAND_ARG_DIGITS)) "    Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
+            TOTP_CLI_COMMAND_ARG_DIGITS)) "    Number of digits to generate, one of: 5, 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
+
+    TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
+        TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX,
+        DOCOPT_ARGUMENT(
+            TOTP_CLI_COMMAND_ARG_SECRET_ENCODING)) "  Token secret encoding, one of " PLAIN_TOKEN_ENCODING_BASE32_NAME
+                                                   ", " PLAIN_TOKEN_ENCODING_BASE64_NAME
+                                                   " " DOCOPT_DEFAULT(
+                                                       PLAIN_TOKEN_ENCODING_BASE32_NAME) "\r\n");
+
     TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
     TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
         TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
         TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
         DOCOPT_ARGUMENT(
         DOCOPT_ARGUMENT(
@@ -83,13 +93,16 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
 
 
     // Read optional arguments
     // Read optional arguments
     bool mask_user_input = true;
     bool mask_user_input = true;
+    PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
     while(args_read_string_and_trim(args, temp_str)) {
     while(args_read_string_and_trim(args, temp_str)) {
         bool parsed = false;
         bool parsed = false;
         if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) &&
         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_digits(token_info, temp_str, args, &parsed) &&
            !totp_cli_try_read_duration(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_unsecure_flag(temp_str, &parsed, &mask_user_input) &&
-           !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) {
+           !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) &&
+           !totp_cli_try_read_plain_token_secret_encoding(
+               temp_str, args, &parsed, &token_secret_encoding)) {
             totp_cli_printf_unknown_argument(temp_str);
             totp_cli_printf_unknown_argument(temp_str);
         }
         }
 
 
@@ -115,31 +128,34 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
 
 
     TOTP_CLI_DELETE_LAST_LINE();
     TOTP_CLI_DELETE_LAST_LINE();
 
 
-    if(!token_info_set_secret(
-           token_info,
-           furi_string_get_cstr(temp_str),
-           furi_string_size(temp_str),
-           plugin_state->iv)) {
-        TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
-        furi_string_secure_free(temp_str);
-        token_info_free(token_info);
-        return;
-    }
-
-    furi_string_secure_free(temp_str);
-
     bool load_generate_token_scene = false;
     bool load_generate_token_scene = false;
     if(plugin_state->current_scene == TotpSceneGenerateToken) {
     if(plugin_state->current_scene == TotpSceneGenerateToken) {
         totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
         totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
         load_generate_token_scene = true;
         load_generate_token_scene = true;
     }
     }
 
 
-    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);
+    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 {
     } else {
-        TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
+        token_info_free(token_info);
+        TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
     }
     }
 
 
     if(load_generate_token_scene) {
     if(load_generate_token_scene) {

+ 1 - 1
cli/commands/delete/delete.c

@@ -3,7 +3,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <ctype.h>
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
-#include "../../../lib/list/list.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"

+ 1 - 1
cli/commands/details/details.c

@@ -1,7 +1,7 @@
 #include "details.h"
 #include "details.h"
 #include <stdlib.h>
 #include <stdlib.h>
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
-#include "../../../lib/list/list.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 "../../cli_helpers.h"
 #include "../../cli_helpers.h"

+ 1 - 1
cli/commands/list/list.c

@@ -1,6 +1,6 @@
 #include "list.h"
 #include "list.h"
 #include <stdlib.h>
 #include <stdlib.h>
-#include "../../../lib/list/list.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 "../../cli_helpers.h"
 #include "../../cli_helpers.h"

+ 1 - 1
cli/commands/move/move.c

@@ -2,7 +2,7 @@
 
 
 #include <stdlib.h>
 #include <stdlib.h>
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
-#include "../../../lib/list/list.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"

+ 2 - 1
cli/commands/pin/pin.c

@@ -2,11 +2,12 @@
 
 
 #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"
 #include "../../cli_helpers.h"
 #include "../../cli_helpers.h"
-#include "../../../lib/polyfills/memset_s.h"
+#include <memset_s.h>
 #include "../../../services/crypto/crypto.h"
 #include "../../../services/crypto/crypto.h"
 #include "../../../ui/scene_director.h"
 #include "../../../ui/scene_director.h"
 
 

+ 3 - 2
cli/commands/timezone/timezone.c

@@ -35,7 +35,7 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
         if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
         if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
             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(tz) == TotpConfigFileUpdateSuccess) {
-                TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", 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();
             }
             }
@@ -50,7 +50,8 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
             TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
             TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
         }
         }
     } else {
     } else {
-        TOTP_CLI_PRINTF_INFO("Current timezone offset is %f\r\n", plugin_state->timezone_offset);
+        TOTP_CLI_PRINTF_INFO(
+            "Current timezone offset is %f\r\n", (double)plugin_state->timezone_offset);
     }
     }
     furi_string_free(temp_str);
     furi_string_free(temp_str);
 }
 }

+ 41 - 34
cli/commands/update/update.c

@@ -1,7 +1,7 @@
 #include "update.h"
 #include "update.h"
 #include <stdlib.h>
 #include <stdlib.h>
 #include <lib/toolbox/args.h>
 #include <lib/toolbox/args.h>
-#include "../../../lib/list/list.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"
@@ -18,17 +18,20 @@ void totp_cli_command_update_docopt_commands() {
 void totp_cli_command_update_docopt_usage() {
 void totp_cli_command_update_docopt_usage() {
     TOTP_CLI_PRINTF(
     TOTP_CLI_PRINTF(
         "  " TOTP_CLI_COMMAND_NAME
         "  " TOTP_CLI_COMMAND_NAME
-        " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL(
+        " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(
             DOCOPT_OPTION(
             DOCOPT_OPTION(
-                TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
+                TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
                 DOCOPT_ARGUMENT(
                 DOCOPT_ARGUMENT(
-                    TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
+                    TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
 }
 }
 
 
 void totp_cli_command_update_docopt_options() {
 void totp_cli_command_update_docopt_options() {
     TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
     TOTP_CLI_PRINTF("  " DOCOPT_OPTION(
         TOTP_CLI_COMMAND_ARG_NAME_PREFIX,
         TOTP_CLI_COMMAND_ARG_NAME_PREFIX,
         DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) "      Token name\r\n");
         DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) "      Token name\r\n");
+
+    TOTP_CLI_PRINTF("  " DOCOPT_SWITCH(
+        TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) "             Update token secret\r\n");
 }
 }
 
 
 static bool
 static bool
@@ -85,6 +88,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
     // Read optional arguments
     // Read optional arguments
     bool mask_user_input = true;
     bool mask_user_input = true;
     bool update_token_secret = false;
     bool update_token_secret = false;
+    PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
     while(args_read_string_and_trim(args, temp_str)) {
     while(args_read_string_and_trim(args, temp_str)) {
         bool parsed = false;
         bool parsed = false;
         if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) &&
         if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) &&
@@ -93,7 +97,9 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
            !totp_cli_try_read_duration(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_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_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_automation_features(token_info, temp_str, args, &parsed) &&
+           !totp_cli_try_read_plain_token_secret_encoding(
+               temp_str, args, &parsed, &token_secret_encoding)) {
             totp_cli_printf_unknown_argument(temp_str);
             totp_cli_printf_unknown_argument(temp_str);
         }
         }
 
 
@@ -105,54 +111,55 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
         }
         }
     }
     }
 
 
+    bool token_secret_read = false;
     if(update_token_secret) {
     if(update_token_secret) {
         // Reading token secret
         // Reading token secret
         furi_string_reset(temp_str);
         furi_string_reset(temp_str);
         TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n");
         TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n");
-        if(!totp_cli_read_line(cli, temp_str, mask_user_input) ||
-           !totp_cli_ensure_authenticated(plugin_state, cli)) {
-            TOTP_CLI_DELETE_LAST_LINE();
+        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");
             TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
-            furi_string_secure_free(temp_str);
-            token_info_free(token_info);
-            return;
         }
         }
+    }
 
 
-        TOTP_CLI_DELETE_LAST_LINE();
+    bool load_generate_token_scene = false;
+    if(plugin_state->current_scene == TotpSceneGenerateToken) {
+        totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
+        load_generate_token_scene = true;
+    }
 
 
+    bool token_secret_set = false;
+    if(update_token_secret && token_secret_read) {
         if(token_info->token != NULL) {
         if(token_info->token != NULL) {
             free(token_info->token);
             free(token_info->token);
         }
         }
-
-        if(!token_info_set_secret(
-               token_info,
-               furi_string_get_cstr(temp_str),
-               furi_string_size(temp_str),
-               plugin_state->iv)) {
+        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");
             TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
-            furi_string_secure_free(temp_str);
-            token_info_free(token_info);
-            return;
         }
         }
     }
     }
 
 
     furi_string_secure_free(temp_str);
     furi_string_secure_free(temp_str);
 
 
-    bool load_generate_token_scene = false;
-    if(plugin_state->current_scene == TotpSceneGenerateToken) {
-        totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
-        load_generate_token_scene = true;
-    }
-
-    list_item->data = token_info;
+    if(!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);
+        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 {
     } else {
-        TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
-        list_item->data = existing_token_info;
         token_info_free(token_info);
         token_info_free(token_info);
     }
     }
 
 

+ 29 - 0
cli/common_command_arguments.c

@@ -108,5 +108,34 @@ bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool*
         return true;
         return true;
     }
     }
 
 
+    return false;
+}
+
+bool totp_cli_try_read_plain_token_secret_encoding(
+    FuriString* arg,
+    FuriString* args,
+    bool* parsed,
+    PlainTokenSecretEncoding* secret_encoding) {
+    if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX) == 0) {
+        if(!args_read_string_and_trim(args, arg)) {
+            totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX);
+        } else {
+            if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) {
+                *secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
+                *parsed = true;
+            } else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) {
+                *secret_encoding = PLAIN_TOKEN_ENCODING_BASE64;
+                *parsed = true;
+            } else {
+                TOTP_CLI_PRINTF_ERROR(
+                    "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX
+                    "\"\r\n",
+                    furi_string_get_cstr(arg));
+            }
+        }
+
+        return true;
+    }
+
     return false;
     return false;
 }
 }

+ 9 - 1
cli/common_command_arguments.h

@@ -15,6 +15,8 @@
 #define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b"
 #define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b"
 #define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature"
 #define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature"
 #define TOTP_CLI_COMMAND_ARG_INDEX "index"
 #define TOTP_CLI_COMMAND_ARG_INDEX "index"
+#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX "-e"
+#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);
@@ -34,4 +36,10 @@ bool totp_cli_try_read_automation_features(
     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(
+    FuriString* arg,
+    FuriString* args,
+    bool* parsed,
+    PlainTokenSecretEncoding* secret_encoding);

+ 3 - 3
lib/base32/base32.c

@@ -17,10 +17,10 @@
 
 
 #include "base32.h"
 #include "base32.h"
 
 
-int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
+size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize) {
     int buffer = 0;
     int buffer = 0;
     int bitsLeft = 0;
     int bitsLeft = 0;
-    int count = 0;
+    size_t count = 0;
     for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) {
     for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) {
         uint8_t ch = *ptr;
         uint8_t ch = *ptr;
         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
@@ -43,7 +43,7 @@ int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
         } else if(ch >= '2' && ch <= '7') {
         } else if(ch >= '2' && ch <= '7') {
             ch -= '2' - 26;
             ch -= '2' - 26;
         } else {
         } else {
-            return -1;
+            return 0;
         }
         }
 
 
         buffer |= ch;
         buffer |= ch;

+ 3 - 2
lib/base32/base32.h

@@ -27,6 +27,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <stdlib.h>
 #include <stdint.h>
 #include <stdint.h>
 
 
 /**
 /**
@@ -34,6 +35,6 @@
  * @param encoded Base-32 encoded bytes
  * @param encoded Base-32 encoded bytes
  * @param[out] result result output buffer
  * @param[out] result result output buffer
  * @param bufSize result output buffer size
  * @param bufSize result output buffer size
- * @return Decoded result length in bytes if successfully decoded; \c -1 otherwise
+ * @return Decoded result length in bytes if successfully decoded; \c 0 otherwise
  */
  */
-int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize);
+size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize);

+ 72 - 0
lib/base64/base64.c

@@ -0,0 +1,72 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ */
+
+#include "base64.h"
+#include <string.h>
+
+static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38,
+                                 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x0,  0x80,
+                                 0x80, 0x80, 0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
+                                 0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,  0x10, 0x11,
+                                 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80,
+                                 0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+                                 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+                                 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33};
+// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static uint8_t get_dtable_value(uint8_t index) {
+    return (index < 43 || index > 122) ? 0x80 : dtable[index - 43];
+}
+
+uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size) {
+    uint8_t *out;
+    uint8_t *pos;
+    uint8_t in[4];
+    uint8_t block[4];
+    uint8_t tmp;
+    size_t i;
+    size_t count;
+    size_t olen;
+
+    count = 0;
+    for(i = 0; i < len; i++) {
+        if(get_dtable_value(src[i]) != 0x80) count++;
+    }
+
+    if(count == 0 || count % 4) return NULL;
+    olen = count / 4 * 3;
+    pos = out = malloc(olen);
+    *out_size = olen;
+    if(out == NULL) return NULL;
+    count = 0;
+    for(i = 0; i < len; i++) {
+        tmp = get_dtable_value(src[i]);
+        if(tmp == 0x80) continue;
+        in[count] = src[i];
+        block[count] = tmp;
+        count++;
+        if(count == 4) {
+            *pos++ = (block[0] << 2) | (block[1] >> 4);
+            *pos++ = (block[1] << 4) | (block[2] >> 2);
+            *pos++ = (block[2] << 6) | block[3];
+            count = 0;
+        }
+    }
+    if(pos > out) {
+        if(in[2] == '=')
+            pos -= 2;
+        else if(in[3] == '=')
+            pos--;
+    }
+    *out_len = pos - out;
+    return out;
+}

+ 14 - 0
lib/base64/base64.h

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/**
+ * @brief Decodes Base-64 encoded bytes into plain bytes.
+ * @param src Base-64 encoded bytes
+ * @param len Base-64 encoded bytes count
+ * @param[out] out_len decoded buffer length
+ * @param[out] out_size decoded buffer allocated size
+ * @return Decoded result buffer if successfully decoded; \c NULL otherwise
+ */
+uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size);

+ 1 - 1
lib/list/list.c → lib/linked_list/linked_list.c

@@ -1,4 +1,4 @@
-#include "list.h"
+#include "linked_list.h"
 
 
 ListNode* list_init_head(void* data) {
 ListNode* list_init_head(void* data) {
     ListNode* new = malloc(sizeof(ListNode));
     ListNode* new = malloc(sizeof(ListNode));

+ 10 - 14
lib/list/list.h → lib/linked_list/linked_list.h

@@ -84,19 +84,15 @@ ListNode* list_insert_at(ListNode* head, uint16_t index, void* data);
 void list_free(ListNode* head);
 void list_free(ListNode* head);
 
 
 #define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
 #define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
-    do {                                          \
-        if(head == NULL) {                        \
-            head = list_init_head(item);          \
-            assert(head != NULL);                 \
-        } else {                                  \
-            assert(list_add(head, item) != NULL); \
-        }                                         \
-    } while(false)
+    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) \
 #define TOTP_LIST_FOREACH(head, node, action) \
-    do {                                      \
-        ListNode* node = head;                \
-        while(node != NULL) {                 \
-            action node = node->next;         \
-        }                                     \
-    } while(false)
+    ListNode* node = head;                    \
+    while(node != NULL) {                     \
+        action node = node->next;             \
+    }

+ 5 - 3
services/config/config.c

@@ -1,7 +1,7 @@
 #include "config.h"
 #include "config.h"
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
-#include "../list/list.h"
+#include <linked_list.h>
 #include "../../types/common.h"
 #include "../../types/common.h"
 #include "../../types/token_info.h"
 #include "../../types/token_info.h"
 #include "../../features_config.h"
 #include "../../features_config.h"
@@ -139,10 +139,11 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
 
 
         furi_string_printf(
         furi_string_printf(
             temp_str,
             temp_str,
-            " # Token hashing algorithm to use during code generation. Supported options are %s, %s and %s. If you are not use which one to use - use %s",
+            " # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s",
             TOTP_TOKEN_ALGO_SHA1_NAME,
             TOTP_TOKEN_ALGO_SHA1_NAME,
             TOTP_TOKEN_ALGO_SHA256_NAME,
             TOTP_TOKEN_ALGO_SHA256_NAME,
             TOTP_TOKEN_ALGO_SHA512_NAME,
             TOTP_TOKEN_ALGO_SHA512_NAME,
+            TOTP_TOKEN_ALGO_STEAM_NAME,
             TOTP_TOKEN_ALGO_SHA1_NAME);
             TOTP_TOKEN_ALGO_SHA1_NAME);
         flipper_format_write_comment(fff_data_file, temp_str);
         flipper_format_write_comment(fff_data_file, temp_str);
         furi_string_printf(
         furi_string_printf(
@@ -152,7 +153,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
 
 
         flipper_format_write_comment_cstr(
         flipper_format_write_comment_cstr(
             fff_data_file,
             fff_data_file,
-            "# How many digits there should be in generated code. Available options are 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6");
+            "# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6");
         furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS);
         furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS);
         flipper_format_write_comment(fff_data_file, temp_str);
         flipper_format_write_comment(fff_data_file, temp_str);
         flipper_format_write_comment_cstr(fff_data_file, " ");
         flipper_format_write_comment_cstr(fff_data_file, " ");
@@ -686,6 +687,7 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
                        tokenInfo,
                        tokenInfo,
                        furi_string_get_cstr(temp_str),
                        furi_string_get_cstr(temp_str),
                        furi_string_size(temp_str),
                        furi_string_size(temp_str),
+                       PLAIN_TOKEN_ENCODING_BASE32,
                        &plugin_state->iv[0])) {
                        &plugin_state->iv[0])) {
                     FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
                     FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
                 } else {
                 } else {

+ 1 - 4
services/hmac/sha512.c

@@ -26,12 +26,9 @@
 #include <stdint.h>
 #include <stdint.h>
 #include <string.h>
 #include <string.h>
 
 
-#ifdef WORDS_BIGENDIAN
-#define SWAP(n) (n)
-#else
 #include "byteswap.h"
 #include "byteswap.h"
+
 #define SWAP(n) swap_uint64(n)
 #define SWAP(n) swap_uint64(n)
-#endif
 
 
 /* This array contains the bytes used to pad the buffer to the next
 /* This array contains the bytes used to pad the buffer to the next
    128-byte boundary.  */
    128-byte boundary.  */

+ 5 - 13
services/totp/totp.c

@@ -11,7 +11,7 @@
 #include "../hmac/byteswap.h"
 #include "../hmac/byteswap.h"
 #include "../../lib/timezone_utils/timezone_utils.h"
 #include "../../lib/timezone_utils/timezone_utils.h"
 
 
-#define HMAC_MAX_SIZE 64
+#define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE
 
 
 /**
 /**
  * @brief Generates the timeblock for a time in seconds.
  * @brief Generates the timeblock for a time in seconds.
@@ -29,19 +29,17 @@ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) {
 /**
 /**
  * @brief Generates an OTP (One Time Password)
  * @brief Generates an OTP (One Time Password)
  * @param algo hashing algorithm to be used
  * @param algo hashing algorithm to be used
- * @param digits desired TOTP code length
  * @param plain_secret plain token secret
  * @param plain_secret plain token secret
  * @param plain_secret_length plain token secret length
  * @param plain_secret_length plain token secret length
  * @param input input data for OTP code generation
  * @param input input data for OTP code generation
  * @return OTP code if code was successfully generated; 0 otherwise
  * @return OTP code if code was successfully generated; 0 otherwise
  */
  */
-uint32_t otp_generate(
+uint64_t otp_generate(
     TOTP_ALGO algo,
     TOTP_ALGO algo,
-    uint8_t digits,
     const uint8_t* plain_secret,
     const uint8_t* plain_secret,
     size_t plain_secret_length,
     size_t plain_secret_length,
     uint64_t input) {
     uint64_t input) {
-    uint8_t hmac[HMAC_MAX_SIZE] = {0};
+    uint8_t hmac[HMAC_MAX_RESULT_SIZE] = {0};
 
 
     uint64_t input_swapped = swap_uint64(input);
     uint64_t input_swapped = swap_uint64(input);
 
 
@@ -55,14 +53,12 @@ uint32_t otp_generate(
     uint64_t i_code =
     uint64_t i_code =
         ((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 |
         ((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 |
          (hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
          (hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
-    i_code %= (uint64_t)pow(10, digits);
 
 
     return i_code;
     return i_code;
 }
 }
 
 
-uint32_t totp_at(
+uint64_t totp_at(
     TOTP_ALGO algo,
     TOTP_ALGO algo,
-    uint8_t digits,
     const uint8_t* plain_secret,
     const uint8_t* plain_secret,
     size_t plain_secret_length,
     size_t plain_secret_length,
     uint64_t for_time,
     uint64_t for_time,
@@ -71,11 +67,7 @@ uint32_t totp_at(
     uint64_t for_time_adjusted =
     uint64_t for_time_adjusted =
         timezone_offset_apply(for_time, timezone_offset_from_hours(timezone));
         timezone_offset_apply(for_time, timezone_offset_from_hours(timezone));
     return otp_generate(
     return otp_generate(
-        algo,
-        digits,
-        plain_secret,
-        plain_secret_length,
-        totp_timecode(interval, for_time_adjusted));
+        algo, plain_secret, plain_secret_length, totp_timecode(interval, for_time_adjusted));
 }
 }
 
 
 static int totp_algo_sha1(
 static int totp_algo_sha1(

+ 1 - 3
services/totp/totp.h

@@ -39,7 +39,6 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512;
 /**
 /**
  * @brief Generates a OTP key using the totp algorithm.
  * @brief Generates a OTP key using the totp algorithm.
  * @param algo hashing algorithm to be used
  * @param algo hashing algorithm to be used
- * @param digits desired TOTP code length
  * @param plain_secret plain token secret
  * @param plain_secret plain token secret
  * @param plain_secret_length plain token secret length
  * @param plain_secret_length plain token secret length
  * @param for_time the time the generated key will be created for
  * @param for_time the time the generated key will be created for
@@ -47,9 +46,8 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512;
  * @param interval token lifetime in seconds
  * @param interval token lifetime in seconds
  * @return TOTP code if code was successfully generated; 0 otherwise
  * @return TOTP code if code was successfully generated; 0 otherwise
  */
  */
-uint32_t totp_at(
+uint64_t totp_at(
     TOTP_ALGO algo,
     TOTP_ALGO algo,
-    uint8_t digits,
     const uint8_t* plain_secret,
     const uint8_t* plain_secret,
     size_t plain_secret_length,
     size_t plain_secret_length,
     uint64_t for_time,
     uint64_t for_time,

+ 1 - 1
types/plugin_state.h

@@ -4,7 +4,7 @@
 #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 "../lib/list/list.h"
+#include <linked_list.h>
 #include "../ui/totp_scenes_enum.h"
 #include "../ui/totp_scenes_enum.h"
 #include "notification_method.h"
 #include "notification_method.h"
 #include "automation_method.h"
 #include "automation_method.h"

+ 38 - 11
types/token_info.c

@@ -1,11 +1,11 @@
-#include <furi_hal.h>
 #include "token_info.h"
 #include "token_info.h"
-#include "stdlib.h"
+#include <furi_hal.h>
+#include <base32.h>
+#include <base64.h>
+#include <memset_s.h>
+#include <strnlen.h>
 #include "common.h"
 #include "common.h"
-#include "../lib/base32/base32.h"
 #include "../services/crypto/crypto.h"
 #include "../services/crypto/crypto.h"
-#include "../lib/polyfills/memset_s.h"
-#include "../lib/polyfills/strnlen.h"
 
 
 TokenInfo* token_info_alloc() {
 TokenInfo* token_info_alloc() {
     TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
     TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
@@ -26,15 +26,32 @@ void token_info_free(TokenInfo* token_info) {
 
 
 bool token_info_set_secret(
 bool token_info_set_secret(
     TokenInfo* token_info,
     TokenInfo* token_info,
-    const char* base32_token_secret,
+    const char* plain_token_secret,
     size_t token_secret_length,
     size_t token_secret_length,
+    PlainTokenSecretEncoding plain_token_secret_encoding,
     const uint8_t* iv) {
     const uint8_t* iv) {
     if(token_secret_length == 0) return false;
     if(token_secret_length == 0) return false;
+    uint8_t* plain_secret;
+    size_t plain_secret_length;
+    size_t plain_secret_size;
+    if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) {
+        plain_secret_size = token_secret_length;
+        plain_secret = malloc(plain_secret_size);
+        furi_check(plain_secret != NULL);
+        plain_secret_length =
+            base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size);
+    } else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) {
+        plain_secret_length = 0;
+        plain_secret = base64_decode(
+            (const uint8_t*)plain_token_secret,
+            token_secret_length,
+            &plain_secret_length,
+            &plain_secret_size);
+        furi_check(plain_secret != NULL);
+    } else {
+        return false;
+    }
 
 
-    uint8_t* plain_secret = malloc(token_secret_length);
-    furi_check(plain_secret != NULL);
-    int plain_secret_length =
-        base32_decode((const uint8_t*)base32_token_secret, plain_secret, token_secret_length);
     bool result;
     bool result;
     if(plain_secret_length > 0) {
     if(plain_secret_length > 0) {
         token_info->token =
         token_info->token =
@@ -44,13 +61,16 @@ bool token_info_set_secret(
         result = false;
         result = false;
     }
     }
 
 
-    memset_s(plain_secret, token_secret_length, 0, token_secret_length);
+    memset_s(plain_secret, plain_secret_size, 0, plain_secret_size);
     free(plain_secret);
     free(plain_secret);
     return result;
     return result;
 }
 }
 
 
 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:
+        token_info->digits = TOTP_5_DIGITS;
+        return true;
     case 6:
     case 6:
         token_info->digits = TOTP_6_DIGITS;
         token_info->digits = TOTP_6_DIGITS;
         return true;
         return true;
@@ -89,6 +109,11 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str)
         return true;
         return true;
     }
     }
 
 
+    if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) {
+        token_info->algo = STEAM;
+        return true;
+    }
+
     return false;
     return false;
 }
 }
 
 
@@ -100,6 +125,8 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
         return TOTP_TOKEN_ALGO_SHA256_NAME;
         return TOTP_TOKEN_ALGO_SHA256_NAME;
     case SHA512:
     case SHA512:
         return TOTP_TOKEN_ALGO_SHA512_NAME;
         return TOTP_TOKEN_ALGO_SHA512_NAME;
+    case STEAM:
+        return TOTP_TOKEN_ALGO_STEAM_NAME;
     default:
     default:
         break;
         break;
     }
     }

+ 24 - 2
types/token_info.h

@@ -7,10 +7,14 @@
 #define TOTP_TOKEN_DURATION_DEFAULT 30
 #define TOTP_TOKEN_DURATION_DEFAULT 30
 
 
 #define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
 #define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
+#define TOTP_TOKEN_ALGO_STEAM_NAME "steam"
 #define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
 #define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
 #define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
 #define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
 #define TOTP_TOKEN_MAX_LENGTH 255
 #define TOTP_TOKEN_MAX_LENGTH 255
 
 
+#define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32"
+#define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64"
+
 #define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none"
 #define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none"
 #define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter"
 #define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter"
 #define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab"
 #define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab"
@@ -19,6 +23,7 @@
 typedef uint8_t TokenHashAlgo;
 typedef uint8_t TokenHashAlgo;
 typedef uint8_t TokenDigitsCount;
 typedef uint8_t TokenDigitsCount;
 typedef uint8_t TokenAutomationFeature;
 typedef uint8_t TokenAutomationFeature;
+typedef uint8_t PlainTokenSecretEncoding;
 
 
 /**
 /**
  * @brief Hashing algorithm to be used to generate token
  * @brief Hashing algorithm to be used to generate token
@@ -37,13 +42,23 @@ enum TokenHashAlgos {
     /**
     /**
      * @brief SHA512 hashing algorithm
      * @brief SHA512 hashing algorithm
      */
      */
-    SHA512
+    SHA512,
+
+    /**
+     * @brief Algorithm used by Steam (Valve)
+     */
+    STEAM
 };
 };
 
 
 /**
 /**
  * @brief Token digits count to be generated.
  * @brief Token digits count to be generated.
  */
  */
 enum TokenDigitsCounts {
 enum TokenDigitsCounts {
+    /**
+     * @brief 6 digits
+     */
+    TOTP_5_DIGITS = 5,
+
     /**
     /**
      * @brief 6 digits
      * @brief 6 digits
      */
      */
@@ -80,6 +95,11 @@ enum TokenAutomationFeatures {
     TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
     TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
 };
 };
 
 
+enum PlainTokenSecretEncodings {
+    PLAIN_TOKEN_ENCODING_BASE32 = 0,
+    PLAIN_TOKEN_ENCODING_BASE64 = 1
+};
+
 #define TOTP_TOKEN_DIGITS_MAX_COUNT 8
 #define TOTP_TOKEN_DIGITS_MAX_COUNT 8
 
 
 /**
 /**
@@ -139,13 +159,15 @@ void token_info_free(TokenInfo* token_info);
  * @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 base32_token_secret plain token secret in Base32 format
  * @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 iv initialization vecor (IV) to be used for encryption
  * @param iv initialization vecor (IV) to be used for encryption
  * @return \c true if token successfully set; \c false otherwise
  * @return \c true if token successfully set; \c false otherwise
  */
  */
 bool token_info_set_secret(
 bool token_info_set_secret(
     TokenInfo* token_info,
     TokenInfo* token_info,
-    const char* base32_token_secret,
+    const char* plain_token_secret,
     size_t token_secret_length,
     size_t token_secret_length,
+    PlainTokenSecretEncoding plain_token_secret_encoding,
     const uint8_t* iv);
     const uint8_t* iv);
 
 
 /**
 /**

+ 24 - 0
ui/fonts/font-info.h

@@ -0,0 +1,24 @@
+#pragma once
+
+/* GENERATED BY https://github.com/pavius/the-dot-factory */
+
+#include <stdint.h>
+
+// This structure describes a single character's display information
+typedef struct {
+    const uint8_t width; // width, in bits (or pixels), of the character
+    const uint16_t
+        offset; // offset of the character's bitmap, in bytes, into the the FONT_INFO's data array
+
+} FONT_CHAR_INFO;
+
+// Describes a single font
+typedef struct {
+    const uint8_t height; // height, in pages (8 pixels), of the font's characters
+    const uint8_t startChar; // the first character in the font (e.g. in charInfo and data)
+    const uint8_t endChar; // the last character in the font
+    const uint8_t spacePixels; // number of pixels that a space character takes up
+    const FONT_CHAR_INFO* charInfo; // pointer to array of char information
+    const uint8_t* data; // pointer to generated array of character visual representation
+
+} FONT_INFO;

+ 940 - 0
ui/fonts/mode-nine/mode-nine.c

@@ -0,0 +1,940 @@
+#include "mode-nine.h"
+
+/* GENERATED BY https://github.com/pavius/the-dot-factory */
+
+/* 
+**  Font data for ModeNine 15pt
+*/
+
+/* Character bitmaps for ModeNine 15pt */
+const uint8_t modeNine_15ptBitmaps[] = {
+    /* @0 '-' (10 pixels wide) */
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+    0x00,
+
+    /* @28 '0' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x33,
+    0x03,
+    0x33,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @56 '1' (10 pixels wide) */
+    0x30,
+    0x00,
+    0x38,
+    0x00,
+    0x3C,
+    0x00,
+    0x3C,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0xFC,
+    0x00,
+    0xFC,
+    0x00,
+
+    /* @84 '2' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x00,
+    0x03,
+    0x80,
+    0x03,
+    0xFC,
+    0x01,
+    0xFE,
+    0x00,
+    0x07,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+
+    /* @112 '3' (10 pixels wide) */
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x80,
+    0x03,
+    0xC0,
+    0x01,
+    0xE0,
+    0x00,
+    0x70,
+    0x00,
+    0xF8,
+    0x00,
+    0xFC,
+    0x01,
+    0x80,
+    0x03,
+    0x00,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @140 '4' (10 pixels wide) */
+    0xE0,
+    0x00,
+    0xF0,
+    0x00,
+    0xF8,
+    0x00,
+    0xDC,
+    0x00,
+    0xCE,
+    0x00,
+    0xC7,
+    0x00,
+    0xC3,
+    0x00,
+    0xC3,
+    0x00,
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0xC0,
+    0x00,
+    0xC0,
+    0x00,
+    0xC0,
+    0x00,
+    0xC0,
+    0x00,
+
+    /* @168 '5' (10 pixels wide) */
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0xFF,
+    0x00,
+    0xFF,
+    0x01,
+    0x80,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @196 '6' (10 pixels wide) */
+    0xF0,
+    0x00,
+    0xFC,
+    0x00,
+    0x0E,
+    0x00,
+    0x06,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0xFF,
+    0x00,
+    0xFF,
+    0x01,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @224 '7' (10 pixels wide) */
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x00,
+    0x03,
+    0x80,
+    0x01,
+    0xC0,
+    0x01,
+    0xE0,
+    0x00,
+    0x30,
+    0x00,
+    0x18,
+    0x00,
+    0x1C,
+    0x00,
+    0x0C,
+    0x00,
+    0x0C,
+    0x00,
+    0x0C,
+    0x00,
+    0x0C,
+    0x00,
+    0x0C,
+    0x00,
+
+    /* @252 '8' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @280 '9' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x07,
+    0x03,
+    0xFE,
+    0x03,
+    0xFC,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x80,
+    0x01,
+    0xC0,
+    0x01,
+    0xFC,
+    0x00,
+    0x3C,
+    0x00,
+
+    /* @308 'B' (10 pixels wide) */
+    0xFF,
+    0x00,
+    0xFF,
+    0x01,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x83,
+    0x03,
+    0xFF,
+    0x01,
+    0xFF,
+    0x01,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x83,
+    0x03,
+    0xFF,
+    0x01,
+    0xFF,
+    0x00,
+
+    /* @336 'C' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @364 'D' (10 pixels wide) */
+    0xFF,
+    0x00,
+    0xFF,
+    0x01,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x83,
+    0x03,
+    0xFF,
+    0x01,
+    0xFF,
+    0x00,
+
+    /* @392 'F' (10 pixels wide) */
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0xFF,
+    0x00,
+    0xFF,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+
+    /* @420 'G' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0xC3,
+    0x03,
+    0xC3,
+    0x03,
+    0x03,
+    0x03,
+    0x07,
+    0x03,
+    0xFE,
+    0x03,
+    0xFC,
+    0x03,
+
+    /* @448 'H' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+
+    /* @476 'J' (10 pixels wide) */
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x00,
+
+    /* @504 'K' (10 pixels wide) */
+    0x83,
+    0x03,
+    0xC3,
+    0x01,
+    0xE3,
+    0x00,
+    0x73,
+    0x00,
+    0x3B,
+    0x00,
+    0x1F,
+    0x00,
+    0x0F,
+    0x00,
+    0x0F,
+    0x00,
+    0x1F,
+    0x00,
+    0x3B,
+    0x00,
+    0x73,
+    0x00,
+    0xE3,
+    0x00,
+    0xC3,
+    0x01,
+    0x83,
+    0x03,
+
+    /* @532 'M' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xCF,
+    0x03,
+    0xFF,
+    0x03,
+    0x7B,
+    0x03,
+    0x33,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+
+    /* @560 'N' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x07,
+    0x03,
+    0x0F,
+    0x03,
+    0x1F,
+    0x03,
+    0x3B,
+    0x03,
+    0x73,
+    0x03,
+    0xE3,
+    0x03,
+    0xC3,
+    0x03,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+
+    /* @588 'P' (10 pixels wide) */
+    0xFF,
+    0x00,
+    0xFF,
+    0x01,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x83,
+    0x03,
+    0xFF,
+    0x01,
+    0xFF,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+    0x03,
+    0x00,
+
+    /* @616 'Q' (10 pixels wide) */
+    0xFC,
+    0x00,
+    0xFE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x33,
+    0x03,
+    0x73,
+    0x03,
+    0xE7,
+    0x03,
+    0xFE,
+    0x01,
+    0xFC,
+    0x03,
+
+    /* @644 'R' (10 pixels wide) */
+    0xFF,
+    0x00,
+    0xFF,
+    0x01,
+    0x83,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x83,
+    0x03,
+    0xFF,
+    0x01,
+    0xFF,
+    0x00,
+    0x1F,
+    0x00,
+    0x3B,
+    0x00,
+    0x73,
+    0x00,
+    0xE3,
+    0x00,
+    0xC3,
+    0x01,
+    0x83,
+    0x03,
+
+    /* @672 'T' (10 pixels wide) */
+    0xFF,
+    0x03,
+    0xFF,
+    0x03,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+
+    /* @700 'V' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x86,
+    0x01,
+    0x86,
+    0x01,
+    0xCC,
+    0x00,
+    0xCC,
+    0x00,
+    0x78,
+    0x00,
+    0x78,
+    0x00,
+    0x30,
+    0x00,
+
+    /* @728 'W' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x33,
+    0x03,
+    0x33,
+    0x03,
+    0x33,
+    0x03,
+    0x33,
+    0x03,
+    0x33,
+    0x03,
+    0x33,
+    0x03,
+    0xFF,
+    0x03,
+    0xFE,
+    0x01,
+
+    /* @756 'X' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xCE,
+    0x01,
+    0xFC,
+    0x00,
+    0xFC,
+    0x00,
+    0xCE,
+    0x01,
+    0x87,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+
+    /* @784 'Y' (10 pixels wide) */
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x03,
+    0x87,
+    0x03,
+    0xCE,
+    0x01,
+    0xFC,
+    0x00,
+    0x78,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+    0x30,
+    0x00,
+};
+
+/* Character descriptors for ModeNine 15pt */
+/* { [Char width in bits], [Offset into modeNine_15ptCharBitmaps in bytes] } */
+const FONT_CHAR_INFO modeNine_15ptDescriptors[] = {
+    {10, 0}, /* - */
+    {0, 0}, /* . */
+    {0, 0}, /* / */
+    {10, 28}, /* 0 */
+    {10, 56}, /* 1 */
+    {10, 84}, /* 2 */
+    {10, 112}, /* 3 */
+    {10, 140}, /* 4 */
+    {10, 168}, /* 5 */
+    {10, 196}, /* 6 */
+    {10, 224}, /* 7 */
+    {10, 252}, /* 8 */
+    {10, 280}, /* 9 */
+    {0, 0}, /* : */
+    {0, 0}, /* ; */
+    {0, 0}, /* < */
+    {0, 0}, /* = */
+    {0, 0}, /* > */
+    {0, 0}, /* ? */
+    {0, 0}, /* @ */
+    {0, 0}, /* A */
+    {10, 308}, /* B */
+    {10, 336}, /* C */
+    {10, 364}, /* D */
+    {0, 0}, /* E */
+    {10, 392}, /* F */
+    {10, 420}, /* G */
+    {10, 448}, /* H */
+    {0, 0}, /* I */
+    {10, 476}, /* J */
+    {10, 504}, /* K */
+    {0, 0}, /* L */
+    {10, 532}, /* M */
+    {10, 560}, /* N */
+    {0, 0}, /* O */
+    {10, 588}, /* P */
+    {10, 616}, /* Q */
+    {10, 644}, /* R */
+    {0, 0}, /* S */
+    {10, 672}, /* T */
+    {0, 0}, /* U */
+    {10, 700}, /* V */
+    {10, 728}, /* W */
+    {10, 756}, /* X */
+    {10, 784}, /* Y */
+};
+
+/* Font information for ModeNine 15pt */
+const FONT_INFO modeNine_15ptFontInfo = {
+    14, /*  Character height */
+    '-', /*  Start character */
+    'Y', /*  End character */
+    2, /*  Width, in pixels, of space character */
+    modeNine_15ptDescriptors, /*  Character descriptor array */
+    modeNine_15ptBitmaps, /*  Character bitmap array */
+};

+ 9 - 0
ui/fonts/mode-nine/mode-nine.h

@@ -0,0 +1,9 @@
+#pragma once
+
+/* GENERATED BY https://github.com/pavius/the-dot-factory */
+
+#include "../font-info.h"
+#include <stdint.h>
+
+/* Font data for ModeNine 15pt */
+extern const FONT_INFO modeNine_15ptFontInfo;

+ 9 - 6
ui/scenes/add_new_token/totp_scene_add_new_token.c

@@ -4,17 +4,17 @@
 #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 "../../../lib/list/list.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 "../../../lib/roll_value/roll_value.h"
+#include <roll_value.h>
 #include "../../../types/nullable.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"};
 char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"};
-char* TOKEN_DIGITS_TEXT_LIST[] = {"6 digits", "8 digits"};
-TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_6_DIGITS, TOTP_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};
 
 
 typedef enum {
 typedef enum {
     TokenNameTextBox,
     TokenNameTextBox,
@@ -95,6 +95,8 @@ void totp_scene_add_new_token_activate(
 
 
     scene_state->screen_y_offset = 0;
     scene_state->screen_y_offset = 0;
 
 
+    scene_state->digits_count_index = 1;
+
     scene_state->input_state = NULL;
     scene_state->input_state = NULL;
     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();
@@ -219,7 +221,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
             totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll);
             totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll);
         } else if(scene_state->selected_control == TokenLengthSelect) {
         } else if(scene_state->selected_control == TokenLengthSelect) {
             totp_roll_value_uint8_t(
             totp_roll_value_uint8_t(
-                &scene_state->digits_count_index, 1, 0, 1, RollOverflowBehaviorRoll);
+                &scene_state->digits_count_index, 1, 0, 2, RollOverflowBehaviorRoll);
         } else if(scene_state->selected_control == TokenDurationSelect) {
         } else if(scene_state->selected_control == TokenDurationSelect) {
             totp_roll_value_uint8_t(&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
             totp_roll_value_uint8_t(&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
             update_duration_text(scene_state);
             update_duration_text(scene_state);
@@ -231,7 +233,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
                 &scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll);
                 &scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll);
         } else if(scene_state->selected_control == TokenLengthSelect) {
         } else if(scene_state->selected_control == TokenLengthSelect) {
             totp_roll_value_uint8_t(
             totp_roll_value_uint8_t(
-                &scene_state->digits_count_index, -1, 0, 1, RollOverflowBehaviorRoll);
+                &scene_state->digits_count_index, -1, 0, 2, RollOverflowBehaviorRoll);
         } else if(scene_state->selected_control == TokenDurationSelect) {
         } else if(scene_state->selected_control == TokenDurationSelect) {
             totp_roll_value_uint8_t(
             totp_roll_value_uint8_t(
                 &scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
                 &scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
@@ -268,6 +270,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
                 tokenInfo,
                 tokenInfo,
                 scene_state->token_secret,
                 scene_state->token_secret,
                 scene_state->token_secret_length,
                 scene_state->token_secret_length,
+                PLAIN_TOKEN_ENCODING_BASE32,
                 &plugin_state->iv[0]);
                 &plugin_state->iv[0]);
 
 
             if(token_secret_set) {
             if(token_secret_set) {

+ 1 - 1
ui/scenes/app_settings/totp_app_settings.c

@@ -8,7 +8,7 @@
 #include "../../constants.h"
 #include "../../constants.h"
 #include "../../../services/config/config.h"
 #include "../../../services/config/config.h"
 #include "../../../services/convert/convert.h"
 #include "../../../services/convert/convert.h"
-#include "../../../lib/roll_value/roll_value.h"
+#include <roll_value.h>
 #include "../../../types/nullable.h"
 #include "../../../types/nullable.h"
 #include "../../../features_config.h"
 #include "../../../features_config.h"
 #ifdef TOTP_BADBT_TYPE_ENABLED
 #ifdef TOTP_BADBT_TYPE_ENABLED

+ 40 - 15
ui/scenes/generate_token/totp_scene_generate_token.c

@@ -19,7 +19,9 @@
 #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"
 #endif
 #endif
+#include "../../fonts/mode-nine/mode-nine.h"
 
 
+static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
 static const uint8_t PROGRESS_BAR_MARGIN = 3;
 static const uint8_t PROGRESS_BAR_MARGIN = 3;
 static const uint8_t PROGRESS_BAR_HEIGHT = 4;
 static const uint8_t PROGRESS_BAR_HEIGHT = 4;
 
 
@@ -121,13 +123,21 @@ static const NotificationSequence*
     return (NotificationSequence*)scene_state->notification_sequence_badusb;
     return (NotificationSequence*)scene_state->notification_sequence_badusb;
 }
 }
 
 
-static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) {
+static void
+    int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) {
     if(i_token_code == OTP_ERROR) {
     if(i_token_code == OTP_ERROR) {
         memset(&str[0], '-', len);
         memset(&str[0], '-', len);
     } else {
     } else {
-        for(int i = len - 1; i >= 0; i--) {
-            str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
-            i_token_code = i_token_code / 10;
+        if(algo == STEAM) {
+            for(uint8_t i = 0; i < len; i++) {
+                str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26];
+                i_token_code = i_token_code / 26;
+            }
+        } else {
+            for(int8_t i = len - 1; i >= 0; i--) {
+                str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
+                i_token_code = i_token_code / 10;
+            }
         }
         }
     }
     }
 
 
@@ -137,6 +147,7 @@ static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount
 static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
 static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
     switch(algo) {
     switch(algo) {
     case SHA1:
     case SHA1:
+    case STEAM:
         return TOTP_ALGO_SHA1;
         return TOTP_ALGO_SHA1;
     case SHA256:
     case SHA256:
         return TOTP_ALGO_SHA256;
         return TOTP_ALGO_SHA256;
@@ -161,6 +172,27 @@ static void update_totp_params(PluginState* const plugin_state) {
     }
     }
 }
 }
 
 
+static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) {
+    uint8_t code_length = scene_state->current_token->digits;
+    uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width;
+    uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels);
+    uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1;
+    uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1);
+    for(uint8_t i = 0; i < code_length; i++) {
+        char ch = scene_state->last_code[i];
+        uint8_t char_index = ch - modeNine_15ptFontInfo.startChar;
+        canvas_draw_xbm(
+            canvas,
+            offset_x,
+            offset_y,
+            char_width,
+            modeNine_15ptFontInfo.height,
+            &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]);
+
+        offset_x += char_width + modeNine_15ptFontInfo.spacePixels;
+    }
+}
+
 void totp_scene_generate_token_init(const PluginState* plugin_state) {
 void totp_scene_generate_token_init(const PluginState* plugin_state) {
     UNUSED(plugin_state);
     UNUSED(plugin_state);
 }
 }
@@ -274,19 +306,19 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
             int_token_to_str(
             int_token_to_str(
                 totp_at(
                 totp_at(
                     get_totp_algo_impl(tokenInfo->algo),
                     get_totp_algo_impl(tokenInfo->algo),
-                    tokenInfo->digits,
                     key,
                     key,
                     key_length,
                     key_length,
                     curr_ts,
                     curr_ts,
                     plugin_state->timezone_offset,
                     plugin_state->timezone_offset,
                     tokenInfo->duration),
                     tokenInfo->duration),
                 scene_state->last_code,
                 scene_state->last_code,
-                tokenInfo->digits);
+                tokenInfo->digits,
+                tokenInfo->algo);
             memset_s(key, key_length, 0, key_length);
             memset_s(key, key_length, 0, key_length);
             free(key);
             free(key);
         } else {
         } else {
             furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
             furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
-            int_token_to_str(0, scene_state->last_code, tokenInfo->digits);
+            int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo);
         }
         }
 
 
         furi_mutex_release(scene_state->last_code_update_sync);
         furi_mutex_release(scene_state->last_code_update_sync);
@@ -322,14 +354,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
         canvas_set_color(canvas, ColorBlack);
         canvas_set_color(canvas, ColorBlack);
     }
     }
 
 
-    canvas_set_font(canvas, FontBigNumbers);
-    canvas_draw_str_aligned(
-        canvas,
-        SCREEN_WIDTH_CENTER,
-        SCREEN_HEIGHT_CENTER,
-        AlignCenter,
-        AlignCenter,
-        scene_state->last_code);
+    draw_totp_code(canvas, scene_state);
 
 
     const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration;
     const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration;
     float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME;
     float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME;

+ 2 - 2
ui/scenes/token_menu/totp_scene_token_menu.c

@@ -6,13 +6,13 @@
 #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 "../../../lib/list/list.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 "../../../types/nullable.h"
-#include "../../../lib/roll_value/roll_value.h"
+#include <roll_value.h>
 
 
 #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
 #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
 #define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1)
 #define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1)

+ 23 - 14
workers/common.c

@@ -3,17 +3,15 @@
 #include <furi_hal.h>
 #include <furi_hal.h>
 #include "../../services/convert/convert.h"
 #include "../../services/convert/convert.h"
 
 
-static const uint8_t hid_number_keys[10] = {
-    HID_KEYBOARD_0,
-    HID_KEYBOARD_1,
-    HID_KEYBOARD_2,
-    HID_KEYBOARD_3,
-    HID_KEYBOARD_4,
-    HID_KEYBOARD_5,
-    HID_KEYBOARD_6,
-    HID_KEYBOARD_7,
-    HID_KEYBOARD_8,
-    HID_KEYBOARD_9};
+static const uint8_t hid_number_keys[] = {
+    HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4,
+    HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9,
+    HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E,
+    HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J,
+    HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_M, HID_KEYBOARD_N, HID_KEYBOARD_O,
+    HID_KEYBOARD_P, HID_KEYBOARD_Q, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T,
+    HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y,
+    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 & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
@@ -49,10 +47,18 @@ void totp_type_code_worker_execute_automation(
     TokenAutomationFeature features) {
     TokenAutomationFeature features) {
     furi_delay_ms(500);
     furi_delay_ms(500);
     uint8_t i = 0;
     uint8_t i = 0;
+    totp_type_code_worker_press_key(
+        HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features);
+
     while(i < string_length && string[i] != 0) {
     while(i < string_length && string[i] != 0) {
-        uint8_t digit = CONVERT_CHAR_TO_DIGIT(string[i]);
-        if(digit > 9) break;
-        uint8_t hid_kb_key = hid_number_keys[digit];
+        uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]);
+        if(char_index > 9) {
+            char_index = string[i] - 0x41 + 10;
+        }
+
+        if(char_index > 35) break;
+
+        uint8_t hid_kb_key = hid_number_keys[char_index];
         totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features);
         totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features);
         furi_delay_ms(get_keystroke_delay(features));
         furi_delay_ms(get_keystroke_delay(features));
         i++;
         i++;
@@ -68,4 +74,7 @@ void totp_type_code_worker_execute_automation(
         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);
     }
     }
+
+    totp_type_code_worker_press_key(
+        HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features);
 }
 }