Explorar el Código

* Updated firmware submodules

* Initial implementation of #11
alex.kopachov hace 3 años
padre
commit
bc9836eff9

+ 1 - 0
scenes/authenticate/totp_scene_authenticate.c

@@ -24,6 +24,7 @@ void totp_scene_authenticate_activate(PluginState* plugin_state) {
     scene_state->code_length = 0;
     memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH);
     plugin_state->current_scene_state = scene_state;
+    memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE);
 }
 
 void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) {

+ 1 - 1
scenes/generate_token/totp_scene_generate_token.c

@@ -130,7 +130,7 @@ void totp_scene_generate_token_activate(
         }
     }
     SceneState* scene_state = malloc(sizeof(SceneState));
-    if(context == 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;

+ 8 - 0
scenes/scene_director.c

@@ -28,6 +28,8 @@ void totp_scene_director_activate_scene(
     case TotpSceneAppSettings:
         totp_scene_app_settings_activate(plugin_state, context);
         break;
+    case TotpSceneNone:
+        break;
     }
 
     plugin_state->current_scene = scene;
@@ -51,6 +53,8 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state
     case TotpSceneAppSettings:
         totp_scene_app_settings_deactivate(plugin_state);
         break;
+    case TotpSceneNone:
+        break;
     }
 }
 
@@ -79,6 +83,8 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_
     case TotpSceneAppSettings:
         totp_scene_app_settings_render(canvas, plugin_state);
         break;
+    case TotpSceneNone:
+        break;
     }
 }
 
@@ -108,6 +114,8 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con
     case TotpSceneAppSettings:
         processing = totp_scene_app_settings_handle_event(event, plugin_state);
         break;
+    case TotpSceneNone:
+        break;
     }
 
     return processing;

+ 1 - 7
scenes/token_menu/totp_scene_token_menu.c

@@ -139,13 +139,7 @@ bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* p
                         dialog_message_show(plugin_state->dialogs, message);
                     dialog_message_free(message);
                     if(dialog_result == DialogMessageButtonRight) {
-                        uint8_t i = 0;
-
-                        ListNode* list_node = plugin_state->tokens_list;
-                        while(i < scene_state->current_token_index && list_node->next != NULL) {
-                            list_node = list_node->next;
-                            i++;
-                        }
+                        ListNode* list_node = list_element_at(plugin_state->tokens_list, scene_state->current_token_index);
 
                         TokenInfo* tokenInfo = list_node->data;
                         token_info_free(tokenInfo);

+ 1 - 0
scenes/totp_scenes_enum.h

@@ -1,6 +1,7 @@
 #pragma once
 
 typedef enum {
+    TotpSceneNone,
     TotpSceneAuthentication,
     TotpSceneGenerateToken,
     TotpSceneAddNewToken,

+ 79 - 0
services/cli/cli.c

@@ -0,0 +1,79 @@
+// Original idea: https://github.com/br0ziliy
+
+#include "cli.h"
+#include <lib/toolbox/args.h>
+#include "commands/list/list.h"
+#include "commands/add/add.h"
+#include "commands/delete/delete.h"
+
+#define TOTP_CLI_COMMAND_NAME "totp"
+
+static void totp_cli_print_unknown_command(FuriString* unknown_command) {
+    printf("Command \"%s\" is unknown. Use \"help\" command to get list of available commands.", furi_string_get_cstr(unknown_command));
+}
+
+static void totp_cli_print_help() {
+    printf("Usage:\r\n");
+    printf("totp <command> <arguments>\r\n");
+    printf("Command list:\r\n");
+    printf("\thelp - print command usage help\r\n");
+    printf("\tlist - list all tokens\r\n");
+    printf("\tdelete <INDEX> [-f] - delete token\r\n");
+    printf("\t\t<INDEX> - token index in the list\r\n");
+    printf("\t\t-f - [OPTIONAL] force command to do not ask user for interactive confirmation\r\n");
+    printf("\tadd <NAME> <SECRET> [-a <ALGO>] [-d <DIGITS>] - add new token\r\n");
+    printf("\t\t<NAME>   - token name\r\n");
+    printf("\t\t<SECRET> - Base32 token secret\r\n");
+    printf("\t\t<ALGO>   - [OPTIONAL] token hashing algorithm, could be one of: sha1, sha256, sha512; default: sha1\r\n");
+    printf("\t\t<DIGITS> - [OPTIONAL] number of digits to generate, one of: 6, 8; default: 6\r\n\r\n");
+}
+
+static void totp_cli_print_unauthenticated() {
+    printf("Pleases enter PIN on your flipper device\r\n");
+}
+
+static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
+    PluginState* plugin_state = (PluginState* )context;
+
+    if (plugin_state->current_scene == TotpSceneAuthentication) {
+        totp_cli_print_unauthenticated();
+        
+        while (plugin_state->current_scene == TotpSceneAuthentication && !cli_cmd_interrupt_received(cli)) {
+            furi_delay_tick(0);
+        }
+
+        if (plugin_state->current_scene == TotpSceneAuthentication) {
+            return;
+        }
+    }
+
+    FuriString* cmd = furi_string_alloc();
+
+    args_read_string_and_trim(args, cmd);
+
+    if(furi_string_cmp_str(cmd, "help") == 0 || furi_string_empty(cmd)) {
+        totp_cli_print_help();
+    } else if(furi_string_cmp_str(cmd, "add") == 0) {
+        totp_cli_handle_add_command(plugin_state, args);
+    } else if(furi_string_cmp_str(cmd, "list") == 0) {
+        totp_cli_handle_list_command(plugin_state);
+    } else if(furi_string_cmp_str(cmd, "delete") == 0) {
+        totp_cli_handle_delete_command(plugin_state, args, cli);
+    } else {
+        totp_cli_print_unknown_command(cmd);
+    }
+
+    furi_string_free(cmd);
+}
+
+void totp_cli_register_command_handler(PluginState* plugin_state) {
+    Cli* cli = furi_record_open(RECORD_CLI);
+    cli_add_command(cli, TOTP_CLI_COMMAND_NAME, CliCommandFlagParallelSafe, totp_cli_handler, plugin_state);
+    furi_record_close(RECORD_CLI);
+}
+
+void totp_cli_unregister_command_handler() {
+    Cli* cli = furi_record_open(RECORD_CLI);
+    cli_delete_command(cli, TOTP_CLI_COMMAND_NAME);
+    furi_record_close(RECORD_CLI);
+}

+ 7 - 0
services/cli/cli.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include <cli/cli.h>
+#include "../../types/plugin_state.h"
+
+void totp_cli_register_command_handler(PluginState* plugin_state);
+void totp_cli_unregister_command_handler();

+ 6 - 0
services/cli/cli_common_helpers.c

@@ -0,0 +1,6 @@
+#include "cli_common_helpers.h"
+#include <cli/cli.h>
+
+void totp_cli_print_invalid_arguments() {
+    printf("Invalid command arguments. use \"help\" command to get list of available commands");
+}

+ 3 - 0
services/cli/cli_common_helpers.h

@@ -0,0 +1,3 @@
+#pragma once
+
+void totp_cli_print_invalid_arguments();

+ 125 - 0
services/cli/commands/add/add.c

@@ -0,0 +1,125 @@
+#include "add.h"
+#include <stdlib.h>
+#include <lib/toolbox/args.h>
+#include "../../../list/list.h"
+#include "../../../../types/token_info.h"
+#include "../../../config/config.h"
+#include "../../cli_common_helpers.h"
+#include "../../../../scenes/scene_director.h"
+
+static bool token_info_set_digits_from_str(TokenInfo* token_info, FuriString* str) {
+    switch(furi_string_get_char(str, 0)) {
+    case '6':
+        token_info->digits = TOTP_6_DIGITS;
+        return true;
+    case '8':
+        token_info->digits = TOTP_8_DIGITS;
+        return true;
+    }
+
+    return false;
+}
+
+static bool token_info_set_algo_from_str(TokenInfo* token_info, FuriString* str) {
+    if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) {
+        token_info->algo = SHA1;
+        return true;
+    } 
+    
+    if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) {
+        token_info->algo = SHA256;
+        return true;
+    } 
+    
+    if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) {
+        token_info->algo = SHA512;
+        return true;
+    }
+
+    return false;
+}
+
+void totp_cli_handle_add_command(PluginState* plugin_state, FuriString* args) {
+    FuriString* temp_str = furi_string_alloc();
+    const char* temp_cstr;
+
+    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;
+    }
+
+    temp_cstr = furi_string_get_cstr(temp_str);
+    token_info->name = malloc(strlen(temp_cstr) + 1);
+    strcpy(token_info->name, temp_cstr);
+
+    // Reading token secret
+    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;
+    }
+
+    temp_cstr = furi_string_get_cstr(temp_str);
+    if (!token_info_set_secret(token_info, temp_cstr, strlen(temp_cstr), plugin_state->iv)) {
+        printf("Token secret seems to be invalid and can not be parsed\r\n");
+        furi_string_free(temp_str);
+        token_info_free(token_info);
+        return;
+    }
+
+    // Read optional arguments
+    while (args_read_string_and_trim(args, temp_str)) {
+        bool parsed = false;
+        if (furi_string_cmpi_str(temp_str, "-a") == 0) {
+            if (!args_read_string_and_trim(args, temp_str)) {
+                printf("Missed value for argument \"-a\"\r\n");
+            } else if (!token_info_set_algo_from_str(token_info, temp_str)) {
+                printf("\"%s\" is incorrect value for argument \"-a\"\r\n", furi_string_get_cstr(temp_str));
+            } else {
+                parsed = true;
+            }
+        } else if (furi_string_cmpi_str(temp_str, "-d") == 0) {
+            if (!args_read_string_and_trim(args, temp_str)) {
+                printf("Missed value for argument \"-d\"\r\n");
+            } else if (!token_info_set_digits_from_str(token_info, temp_str)) {
+                printf("\"%s\" is incorrect value for argument \"-d\"\r\n", furi_string_get_cstr(temp_str));
+            } else {
+                parsed = true;
+            }
+        }
+        if (!parsed) {
+            totp_cli_print_invalid_arguments();
+            furi_string_free(temp_str);
+            token_info_free(token_info);
+            return;
+        }
+    }
+
+    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;
+    }
+
+    if(plugin_state->tokens_list == NULL) {
+        plugin_state->tokens_list = list_init_head(token_info);
+    } else {
+        list_add(plugin_state->tokens_list, token_info);
+    }
+    plugin_state->tokens_count++;
+    totp_config_file_save_new_token(token_info);
+
+    if (load_generate_token_scene) {
+        totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
+    }
+
+    furi_string_free(temp_str);
+
+    printf("Token \"%s\" has been successfully added\r\n", token_info->name);
+}

+ 6 - 0
services/cli/commands/add/add.h

@@ -0,0 +1,6 @@
+#pragma once
+
+#include <cli/cli.h>
+#include "../../../../types/plugin_state.h"
+
+void totp_cli_handle_add_command(PluginState* plugin_state, FuriString* args);

+ 71 - 0
services/cli/commands/delete/delete.c

@@ -0,0 +1,71 @@
+#include "delete.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <lib/toolbox/args.h>
+#include "../../../list/list.h"
+#include "../../../config/config.h"
+#include "../../cli_common_helpers.h"
+#include "../../../../scenes/scene_director.h"
+
+void totp_cli_handle_delete_command(PluginState* plugin_state, FuriString* args, Cli* cli) {
+    int token_number;
+    if (!args_read_int_and_trim(args, &token_number) || token_number <= 0 || token_number > plugin_state->tokens_count) {
+        totp_cli_print_invalid_arguments();
+        return;
+    }
+
+    FuriString* temp_str = furi_string_alloc();
+    bool confirm_needed = true;
+    if (args_read_string_and_trim(args, temp_str)) {
+        if (furi_string_cmpi_str(temp_str, "-f") == 0) {
+            confirm_needed = false;
+        } else {
+            printf("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str));
+            totp_cli_print_invalid_arguments();
+            furi_string_free(temp_str);
+            return;
+        }
+    }
+    furi_string_free(temp_str);
+
+    ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1);
+
+    TokenInfo* token_info = list_node->data;
+
+    bool confirmed = !confirm_needed;
+    if (confirm_needed) {
+        printf("WARNING!\r\n");
+        printf("Token \"%s\" will be permanently deleted without ability to recover it.\r\n", token_info->name);
+        printf("Confirm? [y/n]\r\n");
+        fflush(stdout);
+        char user_pick;
+        do {
+            user_pick = tolower(cli_getc(cli));
+        } while (user_pick != 'y' && user_pick != 'n' && user_pick != 0x0d);
+
+        confirmed = user_pick == 'y' || user_pick == 0x0d;
+    }
+
+    if (confirmed) {
+        bool activate_generate_token_scene = false;
+        if (plugin_state->current_scene == TotpSceneGenerateToken) {
+            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--;
+
+        totp_full_save_config_file(plugin_state);
+        
+        if (activate_generate_token_scene) {
+            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
+        }
+
+        printf("Token \"%s\" has been successfully deleted\r\n", token_info->name);
+        token_info_free(token_info);
+    } else {
+        printf("User not confirmed\r\n");
+    }
+}

+ 6 - 0
services/cli/commands/delete/delete.h

@@ -0,0 +1,6 @@
+#pragma once
+
+#include <cli/cli.h>
+#include "../../../../types/plugin_state.h"
+
+void totp_cli_handle_delete_command(PluginState* plugin_state, FuriString* args, Cli* cli);

+ 52 - 0
services/cli/commands/list/list.c

@@ -0,0 +1,52 @@
+#include "list.h"
+#include <cli/cli.h>
+#include <stdlib.h>
+#include "../../../list/list.h"
+#include "../../../../types/token_info.h"
+#include "../../../config/constants.h"
+
+static char* get_algo_as_cstr(TokenHashAlgo algo) {
+    switch(algo) {
+    case SHA1:
+        return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME;
+    case SHA256:
+        return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME;
+    case SHA512:
+        return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME;
+    }
+
+    return "UNKNOWN";
+}
+
+static uint8_t get_digits_as_int(TokenDigitsCount digits) {
+    switch(digits) {
+    case TOTP_6_DIGITS:
+        return 6;
+    case TOTP_8_DIGITS:
+        return 8;
+    }
+
+    return 6;
+}
+
+void totp_cli_handle_list_command(PluginState* plugin_state) {
+    if (plugin_state->tokens_list == NULL) {
+        printf("There are no tokens");
+        return;
+    }
+
+    ListNode* node = plugin_state->tokens_list;
+
+    printf("+-----+-----------------------------+--------+--------+\r\n");
+    printf("| %-*s | %-*s | %-*s | %-s |\r\n", 3, "#", 27, "Name", 6, "Algo", "Digits");
+    printf("+-----+-----------------------------+--------+--------+\r\n");
+    uint16_t index = 1;
+    while(node != NULL) {
+        TokenInfo* token_info = (TokenInfo* )node->data;
+        token_info_get_digits_count(token_info);
+        printf("| %-3" PRIu16 " | %-27.27s | %-6s | %-6" PRIu8 " |\r\n", index, token_info->name, get_algo_as_cstr(token_info->algo), get_digits_as_int(token_info->digits));
+        node = node->next;
+        index++;
+    }
+    printf("+-----+-----------------------------+--------+--------+\r\n");
+}

+ 5 - 0
services/cli/commands/list/list.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#include "../../../../types/plugin_state.h"
+
+void totp_cli_handle_list_command(PluginState* plugin_state);

+ 6 - 6
services/config/config.c

@@ -10,7 +10,7 @@
 #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
 #define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup"
 
-uint8_t token_info_get_digits_as_int(TokenInfo* token_info) {
+static uint8_t token_info_get_digits_as_int(TokenInfo* token_info) {
     switch(token_info->digits) {
     case TOTP_6_DIGITS:
         return 6;
@@ -21,7 +21,7 @@ uint8_t token_info_get_digits_as_int(TokenInfo* token_info) {
     return 6;
 }
 
-void token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
+static void token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
     switch(digits) {
     case 6:
         token_info->digits = TOTP_6_DIGITS;
@@ -32,7 +32,7 @@ void token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
     }
 }
 
-char* token_info_get_algo_as_cstr(TokenInfo* token_info) {
+static char* token_info_get_algo_as_cstr(TokenInfo* token_info) {
     switch(token_info->algo) {
     case SHA1:
         return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME;
@@ -45,12 +45,12 @@ char* token_info_get_algo_as_cstr(TokenInfo* token_info) {
     return NULL;
 }
 
-void token_info_set_algo_from_str(TokenInfo* token_info, FuriString* str) {
+static void token_info_set_algo_from_str(TokenInfo* token_info, FuriString* str) {
     if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) {
         token_info->algo = SHA1;
-    } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME)) {
+    } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) {
         token_info->algo = SHA256;
-    } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME)) {
+    } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) {
         token_info->algo = SHA512;
     }
 }

+ 4 - 0
services/list/list.c

@@ -44,6 +44,10 @@ ListNode* list_element_at(ListNode* head, uint16_t index) {
 }
 
 ListNode* list_remove(ListNode* head, ListNode* ep) {
+    if (head == NULL) {
+        return NULL;
+    }
+
     if(head == ep) {
         ListNode* new_head = head->next;
         free(head);

+ 12 - 6
totp_app.c

@@ -18,6 +18,7 @@
 #include "scenes/scene_director.h"
 #include "services/ui/constants.h"
 #include "services/crypto/crypto.h"
+#include "services/cli/cli.h"
 
 #define IDLE_TIMEOUT 60000
 
@@ -37,12 +38,15 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
     furi_message_queue_put(event_queue, &event, FuriWaitForever);
 }
 
-static bool totp_state_init(PluginState* const plugin_state) {
+static bool totp_plugin_state_init(PluginState* const plugin_state) {
     plugin_state->gui = furi_record_open(RECORD_GUI);
     plugin_state->notification = furi_record_open(RECORD_NOTIFICATION);
     plugin_state->dialogs = furi_record_open(RECORD_DIALOGS);
+
     totp_config_file_load_base(plugin_state);
 
+    totp_cli_register_command_handler(plugin_state);
+
     totp_scene_director_init_scenes(plugin_state);
 
     if (plugin_state->crypto_verify_data == NULL) {
@@ -77,7 +81,9 @@ static bool totp_state_init(PluginState* const plugin_state) {
     return true;
 }
 
-static void plugin_state_free(PluginState* plugin_state) {
+static void totp_plugin_state_free(PluginState* plugin_state) {
+    totp_cli_unregister_command_handler();
+
     totp_scene_director_deactivate_active_scene(plugin_state);
 
     totp_scene_director_dispose(plugin_state);
@@ -106,16 +112,16 @@ int32_t totp_app() {
     FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
     PluginState* plugin_state = malloc(sizeof(PluginState));
 
-    if (!totp_state_init(plugin_state)) {
+    if (!totp_plugin_state_init(plugin_state)) {
         FURI_LOG_E(LOGGING_TAG, "App state initialization failed\r\n");
-        plugin_state_free(plugin_state);
+        totp_plugin_state_free(plugin_state);
         return 254;
     }
 
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
         FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n");
-        plugin_state_free(plugin_state);
+        totp_plugin_state_free(plugin_state);
         return 255;
     }
 
@@ -155,6 +161,6 @@ int32_t totp_app() {
     view_port_free(view_port);
     furi_message_queue_free(event_queue);
     delete_mutex(&state_mutex);
-    plugin_state_free(plugin_state);
+    totp_plugin_state_free(plugin_state);
     return 0;
 }