Explorar o código

Merge totp from https://github.com/akopachov/flipper-zero_authenticator

Willy-JL %!s(int64=2) %!d(string=hai) anos
pai
achega
d23abe69c5

+ 1 - 3
totp/application.fam

@@ -19,9 +19,6 @@ App(
         Lib(
             name="base32",
         ),
-        Lib(
-            name="base64",
-        ),
         Lib(
             name="timezone_utils",
         ),
@@ -40,6 +37,7 @@ App(
                 "wolfcrypt/src/sha.c",
                 "wolfcrypt/src/sha256.c",
                 "wolfcrypt/src/sha512.c",
+                "wolfcrypt/src/coding.c",
             ],
             cflags=["-Wno-error"],
             cdefines=["HAVE_CONFIG_H"],

+ 2 - 0
totp/config/wolfssl/config.h

@@ -32,3 +32,5 @@
 #define NO_ERROR_STRINGS
 #define NO_OLD_TLS
 #define SINGLE_THREADED
+#define WORD64_AVAILABLE
+#define WOLF_ALLOW_BUILTIN

+ 0 - 73
totp/lib/base64/base64.c

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

+ 0 - 14
totp/lib/base64/base64.h

@@ -1,14 +0,0 @@
-#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);

+ 2 - 2
totp/services/crypto/constants.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "polyfills.h"
+#include <furi_hal_crypto.h>
 
 #define CRYPTO_IV_LENGTH (16)
 #define CRYPTO_SALT_LENGTH (16)
@@ -11,4 +11,4 @@
 #define ACCEPTABLE_CRYPTO_KEY_SLOT_END FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END
 
 #define DEFAULT_CRYPTO_KEY_SLOT ACCEPTABLE_CRYPTO_KEY_SLOT_START
-#define CRYPTO_LATEST_VERSION (3)
+#define CRYPTO_LATEST_VERSION (3)

+ 1 - 2
totp/services/crypto/crypto_v1.c

@@ -7,7 +7,6 @@
 #include <furi_hal_version.h>
 #include "../../types/common.h"
 #include "memset_s.h"
-#include "polyfills.h"
 
 #define CRYPTO_KEY_SLOT (2)
 #define CRYPTO_VERIFY_KEY_LENGTH (16)
@@ -142,4 +141,4 @@ bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings) {
 
     return key_valid;
 }
-#endif
+#endif

+ 0 - 1
totp/services/crypto/crypto_v2.c

@@ -10,7 +10,6 @@
 #include <wolfssl/wolfcrypt/hmac.h>
 #include "memset_s.h"
 #include "constants.h"
-#include "polyfills.h"
 
 #define CRYPTO_ALIGNMENT_FACTOR (16)
 

+ 1 - 2
totp/services/crypto/crypto_v3.c

@@ -10,7 +10,6 @@
 #include <wolfssl/wolfcrypt/pwdbased.h>
 #include "memset_s.h"
 #include "constants.h"
-#include "polyfills.h"
 
 #define CRYPTO_ALIGNMENT_FACTOR (16)
 #define PBKDF2_ITERATIONS_COUNT (200)
@@ -192,4 +191,4 @@ bool totp_crypto_verify_key_v3(const CryptoSettings* crypto_settings) {
     free(decrypted_key);
 
     return key_valid;
-}
+}

+ 0 - 14
totp/services/crypto/polyfills.h

@@ -1,14 +0,0 @@
-#pragma once
-
-#include <furi_hal_crypto.h>
-
-#ifndef FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START
-
-// FW Crypto API is outdated, let's polyfill it
-#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u)
-#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u)
-#define furi_hal_crypto_enclave_ensure_key furi_hal_crypto_verify_key
-#define furi_hal_crypto_enclave_load_key furi_hal_crypto_store_load_key
-#define furi_hal_crypto_enclave_unload_key furi_hal_crypto_store_unload_key
-
-#endif

+ 7 - 7
totp/services/totp/totp.c

@@ -6,15 +6,15 @@
 #include <timezone_utils.h>
 #include "../../config/wolfssl/config.h"
 #include <wolfssl/wolfcrypt/hmac.h>
+#ifdef NO_INLINE
+#include <wolfssl/wolfcrypt/misc.h>
+#else
+#define WOLFSSL_MISC_INCLUDED
+#include <wolfcrypt/src/misc.c>
+#endif
 
 #define HMAC_MAX_RESULT_SIZE WC_SHA512_DIGEST_SIZE
 
-static uint64_t swap_uint64(uint64_t val) {
-    val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);
-    val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);
-    return (val << 32) | (val >> 32);
-}
-
 /**
  * @brief Generates the timeblock for a time in seconds.
  *        Timeblocks are the amount of intervals in a given time. For example,
@@ -43,7 +43,7 @@ uint64_t otp_generate(
     uint64_t input) {
     uint8_t hmac[HMAC_MAX_RESULT_SIZE] = {0};
 
-    uint64_t input_swapped = swap_uint64(input);
+    uint64_t input_swapped = ByteReverseWord64(input);
 
     int hmac_len =
         (*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, &hmac[0]);

+ 16 - 8
totp/types/token_info.c

@@ -1,12 +1,16 @@
 #include "token_info.h"
 #include <furi/core/check.h>
 #include <base32.h>
-#include <base64.h>
+#include "../../config/wolfssl/config.h"
+#include <wolfssl/wolfcrypt/coding.h>
 #include <memset_s.h>
 #include <inttypes.h>
 #include "common.h"
 #include "../services/crypto/crypto_facade.h"
 
+#define ESTIMATE_BASE32_PLAIN_LENGTH(base32_length) ((base32_length)*0.625f)
+#define ESTIMATE_BASE64_PLAIN_LENGTH(base64_length) ((base64_length)*0.75f)
+
 TokenInfo* token_info_alloc() {
     TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
     furi_check(tokenInfo != NULL);
@@ -33,19 +37,23 @@ bool token_info_set_secret(
     size_t plain_secret_length;
     size_t plain_secret_size;
     if(plain_token_secret_encoding == PlainTokenSecretEncodingBase32) {
-        plain_secret_size = token_secret_length;
+        plain_secret_size = ESTIMATE_BASE32_PLAIN_LENGTH(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 == PlainTokenSecretEncodingBase64) {
-        plain_secret_length = 0;
-        plain_secret = base64_decode(
-            (const uint8_t*)plain_token_secret,
-            token_secret_length,
-            &plain_secret_length,
-            &plain_secret_size);
+        plain_secret_size = ESTIMATE_BASE64_PLAIN_LENGTH(token_secret_length);
+        plain_secret_length = plain_secret_size;
+        plain_secret = malloc(plain_secret_size);
         furi_check(plain_secret != NULL);
+        if(Base64_Decode(
+               (const uint8_t*)plain_token_secret,
+               token_secret_length,
+               plain_secret,
+               &plain_secret_length) != 0) {
+            plain_secret_length = 0;
+        }
     } else {
         return false;
     }

+ 53 - 53
totp/ui/scenes/add_new_token/totp_scene_add_new_token.c

@@ -315,65 +315,65 @@ bool totp_scene_add_new_token_handle_event(
                     RollOverflowBehaviorRoll);
             }
             break;
-        case InputKeyOk:
-            break;
-        case InputKeyBack:
-            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
-            break;
-        default:
-            break;
-        }
-    } else if(event->input.type == InputTypeShort && event->input.key == InputKeyOk) {
-        switch(scene_state->selected_control) {
-        case TokenNameTextBox:
-            ask_user_input(
-                plugin_state,
-                "Token name",
-                &scene_state->token_name,
-                &scene_state->token_name_length);
-            break;
-        case TokenSecretTextBox:
-            ask_user_input(
-                plugin_state,
-                "Token secret",
-                &scene_state->token_secret,
-                &scene_state->token_secret_length);
-            break;
-        case TokenAlgoSelect:
-            break;
-        case TokenLengthSelect:
-            break;
-        case TokenDurationOrCounterSelect:
-            if(scene_state->type == TokenTypeHOTP) {
+        case InputKeyOk: {
+            switch(scene_state->selected_control) {
+            case TokenNameTextBox:
                 ask_user_input(
                     plugin_state,
-                    "Initial counter",
-                    &scene_state->initial_counter,
-                    &scene_state->initial_counter_length);
+                    "Token name",
+                    &scene_state->token_name,
+                    &scene_state->token_name_length);
+                break;
+            case TokenSecretTextBox:
+                ask_user_input(
+                    plugin_state,
+                    "Token secret",
+                    &scene_state->token_secret,
+                    &scene_state->token_secret_length);
+                break;
+            case TokenAlgoSelect:
+                break;
+            case TokenLengthSelect:
+                break;
+            case TokenDurationOrCounterSelect:
+                if(scene_state->type == TokenTypeHOTP) {
+                    ask_user_input(
+                        plugin_state,
+                        "Initial counter",
+                        &scene_state->initial_counter,
+                        &scene_state->initial_counter_length);
+                }
+                break;
+            case ConfirmButton: {
+                struct TotpAddContext add_context = {
+                    .scene_state = scene_state, .crypto_settings = &plugin_state->crypto_settings};
+                TokenInfoIteratorContext* iterator_context =
+                    totp_config_get_token_iterator_context(plugin_state);
+                TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token(
+                    iterator_context, &add_token_handler, &add_context);
+
+                if(add_result == TotpIteratorUpdateTokenResultSuccess) {
+                    totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+                } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
+                    show_invalid_field_message(
+                        plugin_state, TokenSecretTextBox, "Token secret is invalid");
+                } else if(add_result == TotpIteratorUpdateTokenResultInvalidCounter) {
+                    show_invalid_field_message(
+                        plugin_state, TokenDurationOrCounterSelect, "Initial counter is invalid");
+                } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
+                    totp_dialogs_config_updating_error(plugin_state);
+                }
+
+                break;
             }
-            break;
-        case ConfirmButton: {
-            struct TotpAddContext add_context = {
-                .scene_state = scene_state, .crypto_settings = &plugin_state->crypto_settings};
-            TokenInfoIteratorContext* iterator_context =
-                totp_config_get_token_iterator_context(plugin_state);
-            TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token(
-                iterator_context, &add_token_handler, &add_context);
-
-            if(add_result == TotpIteratorUpdateTokenResultSuccess) {
-                totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
-            } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
-                show_invalid_field_message(
-                    plugin_state, TokenSecretTextBox, "Token secret is invalid");
-            } else if(add_result == TotpIteratorUpdateTokenResultInvalidCounter) {
-                show_invalid_field_message(
-                    plugin_state, TokenDurationOrCounterSelect, "Initial counter is invalid");
-            } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
-                totp_dialogs_config_updating_error(plugin_state);
+            default:
+                break;
             }
-
             break;
         }
+        case InputKeyBack:
+            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+            break;
         default:
             break;
         }

+ 29 - 28
totp/ui/scenes/app_settings/totp_app_settings.c

@@ -332,6 +332,35 @@ bool totp_scene_app_settings_handle_event(
             }
             break;
         case InputKeyOk:
+            if(scene_state->selected_control == ConfirmButton) {
+                plugin_state->timezone_offset = (float)scene_state->tz_offset_hours +
+                                                (float)scene_state->tz_offset_minutes / 60.0f;
+
+                plugin_state->notification_method =
+                    (scene_state->notification_sound ? NotificationMethodSound :
+                                                       NotificationMethodNone) |
+                    (scene_state->notification_vibro ? NotificationMethodVibro :
+                                                       NotificationMethodNone);
+
+                plugin_state->automation_method = scene_state->automation_method;
+                plugin_state->active_font_index = scene_state->active_font_index;
+                plugin_state->automation_kb_layout = scene_state->automation_kb_layout;
+
+                if(!totp_config_file_update_user_settings(plugin_state)) {
+                    totp_dialogs_config_updating_error(plugin_state);
+                    return false;
+                }
+
+#ifdef TOTP_BADBT_AUTOMATION_ENABLED
+                if((scene_state->automation_method & AutomationMethodBadBt) == 0 &&
+                   plugin_state->bt_type_code_worker_context != NULL) {
+                    totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+                    plugin_state->bt_type_code_worker_context = NULL;
+                }
+#endif
+
+                totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
+            }
             break;
         case InputKeyBack: {
             totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
@@ -340,34 +369,6 @@ bool totp_scene_app_settings_handle_event(
         default:
             break;
         }
-    } else if(
-        event->input.type == InputTypeShort && event->input.key == InputKeyOk &&
-        scene_state->selected_control == ConfirmButton) {
-        plugin_state->timezone_offset =
-            (float)scene_state->tz_offset_hours + (float)scene_state->tz_offset_minutes / 60.0f;
-
-        plugin_state->notification_method =
-            (scene_state->notification_sound ? NotificationMethodSound : NotificationMethodNone) |
-            (scene_state->notification_vibro ? NotificationMethodVibro : NotificationMethodNone);
-
-        plugin_state->automation_method = scene_state->automation_method;
-        plugin_state->active_font_index = scene_state->active_font_index;
-        plugin_state->automation_kb_layout = scene_state->automation_kb_layout;
-
-        if(!totp_config_file_update_user_settings(plugin_state)) {
-            totp_dialogs_config_updating_error(plugin_state);
-            return false;
-        }
-
-#ifdef TOTP_BADBT_AUTOMATION_ENABLED
-        if((scene_state->automation_method & AutomationMethodBadBt) == 0 &&
-           plugin_state->bt_type_code_worker_context != NULL) {
-            totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
-            plugin_state->bt_type_code_worker_context = NULL;
-        }
-#endif
-
-        totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
     }
 
     return true;

+ 35 - 35
totp/ui/scenes/authenticate/totp_scene_authenticate.c

@@ -86,7 +86,7 @@ bool totp_scene_authenticate_handle_event(
     }
 
     SceneState* scene_state = plugin_state->current_scene_state;
-    if(event->input.type == InputTypeShort) {
+    if(event->input.type == InputTypePress) {
         switch(event->input.key) {
         case InputKeyUp:
             if(scene_state->code_length < MAX_CODE_LENGTH) {
@@ -112,8 +112,41 @@ bool totp_scene_authenticate_handle_event(
                 scene_state->code_length++;
             }
             break;
-        case InputKeyOk:
+        case InputKeyOk: {
+            CryptoSeedIVResult seed_result = totp_crypto_seed_iv(
+                &plugin_state->crypto_settings,
+                &scene_state->code_input[0],
+                scene_state->code_length);
+
+            if(seed_result & CryptoSeedIVResultFlagSuccess &&
+               seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
+                totp_config_file_update_crypto_signatures(plugin_state);
+            }
+
+            if(totp_crypto_verify_key(&plugin_state->crypto_settings)) {
+                totp_config_file_ensure_latest_encryption(
+                    plugin_state, &scene_state->code_input[0], scene_state->code_length);
+                totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+            } else {
+                memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH);
+                memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH);
+                scene_state->code_length = 0;
+
+                DialogMessage* message = dialog_message_alloc();
+                dialog_message_set_buttons(message, "Try again", NULL, NULL);
+                dialog_message_set_header(
+                    message,
+                    "You entered\ninvalid PIN",
+                    SCREEN_WIDTH_CENTER - 25,
+                    SCREEN_HEIGHT_CENTER - 5,
+                    AlignCenter,
+                    AlignCenter);
+                dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
+                dialog_message_show(plugin_state->dialogs_app, message);
+                dialog_message_free(message);
+            }
             break;
+        }
         case InputKeyBack:
             if(scene_state->code_length > 0) {
                 scene_state->code_input[scene_state->code_length - 1] = 0;
@@ -123,39 +156,6 @@ bool totp_scene_authenticate_handle_event(
         default:
             break;
         }
-    } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) {
-        CryptoSeedIVResult seed_result = totp_crypto_seed_iv(
-            &plugin_state->crypto_settings, &scene_state->code_input[0], scene_state->code_length);
-
-        if(seed_result & CryptoSeedIVResultFlagSuccess &&
-           seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
-            totp_config_file_update_crypto_signatures(plugin_state);
-        }
-
-        if(totp_crypto_verify_key(&plugin_state->crypto_settings)) {
-            FURI_LOG_D(LOGGING_TAG, "PIN is valid");
-            totp_config_file_ensure_latest_encryption(
-                plugin_state, &scene_state->code_input[0], scene_state->code_length);
-            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
-        } else {
-            FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid");
-            memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH);
-            memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH);
-            scene_state->code_length = 0;
-
-            DialogMessage* message = dialog_message_alloc();
-            dialog_message_set_buttons(message, "Try again", NULL, NULL);
-            dialog_message_set_header(
-                message,
-                "You entered\ninvalid PIN",
-                SCREEN_WIDTH_CENTER - 25,
-                SCREEN_HEIGHT_CENTER - 5,
-                AlignCenter,
-                AlignCenter);
-            dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
-            dialog_message_show(plugin_state->dialogs_app, message);
-            dialog_message_free(message);
-        }
     }
 
     return true;

+ 1 - 2
totp/ui/scenes/generate_token/totp_scene_generate_token.c

@@ -424,14 +424,13 @@ bool totp_scene_generate_token_handle_event(
             break;
         }
         case InputKeyOk:
+            totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
             break;
         case InputKeyBack:
             break;
         default:
             break;
         }
-    } else if(event->input.type == InputTypeShort && event->input.key == InputKeyOk) {
-        totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
     }
 
     return true;

+ 52 - 52
totp/ui/scenes/token_menu/totp_scene_token_menu.c

@@ -116,65 +116,65 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
             break;
         case InputKeyLeft:
             break;
-        case InputKeyOk:
-            break;
-        case InputKeyBack: {
-            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
-            break;
-        }
-        default:
-            break;
-        }
-    } else if(event->input.type == InputTypeShort && event->input.key == InputKeyOk) {
-        switch(scene_state->selected_control) {
-        case AddNewToken: {
+        case InputKeyOk: {
+            switch(scene_state->selected_control) {
+            case AddNewToken: {
 #ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED
-            totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken);
+                totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken);
 #else
-            DialogMessage* message = dialog_message_alloc();
-            dialog_message_set_buttons(message, "Back", NULL, NULL);
-            dialog_message_set_header(message, "Information", 0, 0, AlignLeft, AlignTop);
-            dialog_message_set_text(
-                message,
-                "Read here\nhttps://t.ly/8ZOtj\n how to add new token",
-                SCREEN_WIDTH_CENTER,
-                SCREEN_HEIGHT_CENTER,
-                AlignCenter,
-                AlignCenter);
-            dialog_message_show(plugin_state->dialogs_app, message);
-            dialog_message_free(message);
-#endif
-            break;
-        }
-        case DeleteToken: {
-            DialogMessage* message = dialog_message_alloc();
-            dialog_message_set_buttons(message, "No", NULL, "Yes");
-            dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop);
-            dialog_message_set_text(
-                message,
-                "Are you sure want to delete?",
-                SCREEN_WIDTH_CENTER,
-                SCREEN_HEIGHT_CENTER,
-                AlignCenter,
-                AlignCenter);
-            DialogMessageButton dialog_result =
+                DialogMessage* message = dialog_message_alloc();
+                dialog_message_set_buttons(message, "Back", NULL, NULL);
+                dialog_message_set_header(message, "Information", 0, 0, AlignLeft, AlignTop);
+                dialog_message_set_text(
+                    message,
+                    "Read here\nhttps://t.ly/8ZOtj\nhow to add new token",
+                    SCREEN_WIDTH_CENTER,
+                    SCREEN_HEIGHT_CENTER,
+                    AlignCenter,
+                    AlignCenter);
                 dialog_message_show(plugin_state->dialogs_app, message);
-            dialog_message_free(message);
-            TokenInfoIteratorContext* iterator_context =
-                totp_config_get_token_iterator_context(plugin_state);
-            if(dialog_result == DialogMessageButtonRight &&
-               totp_token_info_iterator_get_total_count(iterator_context) > 0) {
-                if(!totp_token_info_iterator_remove_current_token_info(iterator_context)) {
-                    totp_dialogs_config_updating_error(plugin_state);
-                    return false;
-                }
+                dialog_message_free(message);
+#endif
+                break;
+            }
+            case DeleteToken: {
+                DialogMessage* message = dialog_message_alloc();
+                dialog_message_set_buttons(message, "No", NULL, "Yes");
+                dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop);
+                dialog_message_set_text(
+                    message,
+                    "Are you sure want to delete?",
+                    SCREEN_WIDTH_CENTER,
+                    SCREEN_HEIGHT_CENTER,
+                    AlignCenter,
+                    AlignCenter);
+                DialogMessageButton dialog_result =
+                    dialog_message_show(plugin_state->dialogs_app, message);
+                dialog_message_free(message);
+                TokenInfoIteratorContext* iterator_context =
+                    totp_config_get_token_iterator_context(plugin_state);
+                if(dialog_result == DialogMessageButtonRight &&
+                   totp_token_info_iterator_get_total_count(iterator_context) > 0) {
+                    if(!totp_token_info_iterator_remove_current_token_info(iterator_context)) {
+                        totp_dialogs_config_updating_error(plugin_state);
+                        return false;
+                    }
 
-                totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+                    totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+                }
+                break;
+            }
+            case AppSettings: {
+                totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings);
+                break;
+            }
+            default:
+                break;
             }
             break;
         }
-        case AppSettings: {
-            totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings);
+        case InputKeyBack: {
+            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
             break;
         }
         default:

+ 6 - 4
totp/workers/type_code_common.c

@@ -88,6 +88,8 @@ void totp_type_code_worker_execute_automation(
         return;
     }
 
+    uint32_t keystroke_delay = get_keystroke_delay(features);
+
     while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) {
         uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char);
         if(char_index > 9) {
@@ -105,18 +107,18 @@ void totp_type_code_worker_execute_automation(
         }
 
         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(keystroke_delay);
         i++;
     }
 
     if(features & TokenAutomationFeatureEnterAtTheEnd) {
-        furi_delay_ms(get_keystroke_delay(features));
+        furi_delay_ms(keystroke_delay);
         totp_type_code_worker_press_key(
             HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features);
     }
 
     if(features & TokenAutomationFeatureTabAtTheEnd) {
-        furi_delay_ms(get_keystroke_delay(features));
+        furi_delay_ms(keystroke_delay);
         totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features);
     }
-}
+}