Alexander Kopachov %!s(int64=2) %!d(string=hai) anos
pai
achega
446e0a1ea4

+ 3 - 11
cli/commands/add/add.c

@@ -11,9 +11,7 @@
 struct TotpAddContext {
     FuriString* args;
     Cli* cli;
-    uint8_t* iv;
-    uint8_t crypto_version;
-    uint8_t crypto_key_slot;
+    const CryptoSettings* crypto_settings;
 };
 
 enum TotpIteratorUpdateTokenResultsEx {
@@ -70,9 +68,7 @@ static TotpIteratorUpdateTokenResult
         furi_string_get_cstr(temp_str),
         furi_string_size(temp_str),
         token_secret_encoding,
-        context_t->iv,
-        context_t->crypto_version,
-        context_t->crypto_key_slot);
+        context_t->crypto_settings);
 
     furi_string_secure_free(temp_str);
 
@@ -171,11 +167,7 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
     TOTP_CLI_LOCK_UI(plugin_state);
 
     struct TotpAddContext add_context = {
-        .args = args,
-        .cli = cli,
-        .iv = &plugin_state->iv[0],
-        .crypto_version = plugin_state->crypto_version,
-        .crypto_key_slot = plugin_state->crypto_key_slot};
+        .args = args, .cli = cli, .crypto_settings = &plugin_state->crypto_settings};
     TotpIteratorUpdateTokenResult add_result =
         totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context);
 

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

@@ -105,7 +105,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
 
     bool do_change = false;
     bool do_remove = false;
-    uint8_t crypto_key_slot = plugin_state->crypto_key_slot;
+    uint8_t crypto_key_slot = plugin_state->crypto_settings.crypto_key_slot;
 
     bool arguments_parsed = true;
     while(args_read_string_and_trim(args, temp_str)) {

+ 3 - 11
cli/commands/update/update.c

@@ -13,9 +13,7 @@
 struct TotpUpdateContext {
     FuriString* args;
     Cli* cli;
-    uint8_t* iv;
-    uint8_t crypto_version;
-    uint8_t crypto_key_slot;
+    const CryptoSettings* crypto_settings;
 };
 
 enum TotpIteratorUpdateTokenResultsEx {
@@ -98,9 +96,7 @@ static TotpIteratorUpdateTokenResult
                furi_string_get_cstr(temp_str),
                furi_string_size(temp_str),
                token_secret_encoding,
-               context_t->iv,
-               context_t->crypto_version,
-               context_t->crypto_key_slot)) {
+               context_t->crypto_settings)) {
             furi_string_secure_free(temp_str);
             return TotpIteratorUpdateTokenResultInvalidSecret;
         }
@@ -155,11 +151,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
     totp_token_info_iterator_go_to(iterator_context, token_number - 1);
 
     struct TotpUpdateContext update_context = {
-        .args = args,
-        .cli = cli,
-        .iv = &plugin_state->iv[0],
-        .crypto_version = plugin_state->crypto_version,
-        .crypto_key_slot = plugin_state->crypto_key_slot};
+        .args = args, .cli = cli, .crypto_settings = &plugin_state->crypto_settings};
     TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token(
         iterator_context, &update_token_handler, &update_context);
 

+ 45 - 48
services/config/config.c

@@ -375,7 +375,7 @@ bool totp_config_file_load(PluginState* const plugin_state) {
             break;
         }
 
-        plugin_state->crypto_version = tmp_uint32;
+        plugin_state->crypto_settings.crypto_version = tmp_uint32;
 
         if(!flipper_format_rewind(fff_data_file)) {
             break;
@@ -388,7 +388,7 @@ bool totp_config_file_load(PluginState* const plugin_state) {
             break;
         }
 
-        plugin_state->crypto_key_slot = tmp_uint32;
+        plugin_state->crypto_settings.crypto_key_slot = tmp_uint32;
 
         if(!flipper_format_rewind(fff_data_file)) {
             break;
@@ -397,7 +397,7 @@ bool totp_config_file_load(PluginState* const plugin_state) {
         if(!flipper_format_read_hex(
                fff_data_file,
                TOTP_CONFIG_KEY_BASE_IV,
-               &plugin_state->base_iv[0],
+               &plugin_state->crypto_settings.base_iv[0],
                CRYPTO_IV_LENGTH)) {
             FURI_LOG_D(LOGGING_TAG, "Missing base IV");
         }
@@ -410,22 +410,23 @@ bool totp_config_file_load(PluginState* const plugin_state) {
         if(flipper_format_get_value_count(
                fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) &&
            crypto_size > 0) {
-            plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size);
-            furi_check(plugin_state->crypto_verify_data != NULL);
-            plugin_state->crypto_verify_data_length = crypto_size;
+            plugin_state->crypto_settings.crypto_verify_data =
+                malloc(sizeof(uint8_t) * crypto_size);
+            furi_check(plugin_state->crypto_settings.crypto_verify_data != NULL);
+            plugin_state->crypto_settings.crypto_verify_data_length = crypto_size;
             if(!flipper_format_read_hex(
                    fff_data_file,
                    TOTP_CONFIG_KEY_CRYPTO_VERIFY,
-                   plugin_state->crypto_verify_data,
+                   plugin_state->crypto_settings.crypto_verify_data,
                    crypto_size)) {
                 FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token");
-                free(plugin_state->crypto_verify_data);
-                plugin_state->crypto_verify_data = NULL;
-                plugin_state->crypto_verify_data_length = 0;
+                free(plugin_state->crypto_settings.crypto_verify_data);
+                plugin_state->crypto_settings.crypto_verify_data = NULL;
+                plugin_state->crypto_settings.crypto_verify_data_length = 0;
             }
         } else {
-            plugin_state->crypto_verify_data = NULL;
-            plugin_state->crypto_verify_data_length = 0;
+            plugin_state->crypto_settings.crypto_verify_data = NULL;
+            plugin_state->crypto_settings.crypto_verify_data_length = 0;
         }
 
         if(!flipper_format_rewind(fff_data_file)) {
@@ -443,8 +444,11 @@ bool totp_config_file_load(PluginState* const plugin_state) {
         }
 
         if(!flipper_format_read_bool(
-               fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
-            plugin_state->pin_set = true;
+               fff_data_file,
+               TOTP_CONFIG_KEY_PINSET,
+               &plugin_state->crypto_settings.pin_required,
+               1)) {
+            plugin_state->crypto_settings.pin_required = true;
         }
 
         if(!flipper_format_rewind(fff_data_file)) {
@@ -498,9 +502,7 @@ bool totp_config_file_load(PluginState* const plugin_state) {
             totp_token_info_iterator_alloc(
                 storage,
                 plugin_state->config_file_context->config_file,
-                plugin_state->iv,
-                plugin_state->crypto_version,
-                plugin_state->crypto_key_slot);
+                &plugin_state->crypto_settings);
         result = true;
     } while(false);
 
@@ -513,33 +515,39 @@ bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state)
     flipper_format_rewind(config_file);
     bool update_result = false;
     do {
-        uint32_t tmp_uint32 = plugin_state->crypto_version;
+        uint32_t tmp_uint32 = plugin_state->crypto_settings.crypto_version;
         if(!flipper_format_insert_or_update_uint32(
                config_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1)) {
             break;
         }
 
-        tmp_uint32 = plugin_state->crypto_key_slot;
+        tmp_uint32 = plugin_state->crypto_settings.crypto_key_slot;
         if(!flipper_format_insert_or_update_uint32(
                config_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1)) {
             break;
         }
 
         if(!flipper_format_insert_or_update_hex(
-               config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, CRYPTO_IV_LENGTH)) {
+               config_file,
+               TOTP_CONFIG_KEY_BASE_IV,
+               plugin_state->crypto_settings.base_iv,
+               CRYPTO_IV_LENGTH)) {
             break;
         }
 
         if(!flipper_format_insert_or_update_hex(
                config_file,
                TOTP_CONFIG_KEY_CRYPTO_VERIFY,
-               plugin_state->crypto_verify_data,
-               plugin_state->crypto_verify_data_length)) {
+               plugin_state->crypto_settings.crypto_verify_data,
+               plugin_state->crypto_settings.crypto_verify_data_length)) {
             break;
         }
 
         if(!flipper_format_insert_or_update_bool(
-               config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
+               config_file,
+               TOTP_CONFIG_KEY_PINSET,
+               &plugin_state->crypto_settings.pin_required,
+               1)) {
             break;
         }
 
@@ -581,24 +589,20 @@ bool totp_config_file_update_encryption(
         return false;
     }
 
-    uint8_t old_iv[CRYPTO_IV_LENGTH];
-    memcpy(&old_iv[0], &plugin_state->iv[0], CRYPTO_IV_LENGTH);
-
-    uint8_t old_crypto_key_slot = plugin_state->crypto_key_slot;
-    uint8_t old_crypto_version = plugin_state->crypto_version;
+    CryptoSettings old_crypto_settings = plugin_state->crypto_settings;
 
-    memset(&plugin_state->iv[0], 0, CRYPTO_IV_LENGTH);
-    memset(&plugin_state->base_iv[0], 0, CRYPTO_IV_LENGTH);
-    if(plugin_state->crypto_verify_data != NULL) {
-        free(plugin_state->crypto_verify_data);
-        plugin_state->crypto_verify_data = NULL;
+    memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH);
+    memset(&plugin_state->crypto_settings.base_iv[0], 0, CRYPTO_IV_LENGTH);
+    if(plugin_state->crypto_settings.crypto_verify_data != NULL) {
+        free(plugin_state->crypto_settings.crypto_verify_data);
+        plugin_state->crypto_settings.crypto_verify_data = NULL;
     }
 
-    plugin_state->crypto_key_slot = new_crypto_key_slot;
-    plugin_state->crypto_version = CRYPTO_LATEST_VERSION;
+    plugin_state->crypto_settings.crypto_key_slot = new_crypto_key_slot;
+    plugin_state->crypto_settings.crypto_version = CRYPTO_LATEST_VERSION;
 
-    CryptoSeedIVResult seed_result =
-        totp_crypto_seed_iv(plugin_state, new_pin_length > 0 ? new_pin : NULL, new_pin_length);
+    CryptoSeedIVResult seed_result = totp_crypto_seed_iv(
+        &plugin_state->crypto_settings, new_pin_length > 0 ? new_pin : NULL, new_pin_length);
     if(seed_result & CryptoSeedIVResultFlagSuccess &&
        seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData &&
        !totp_config_file_update_crypto_signatures(plugin_state)) {
@@ -649,21 +653,14 @@ bool totp_config_file_update_encryption(
 
                 size_t plain_token_length;
                 uint8_t* plain_token = totp_crypto_decrypt(
-                    encrypted_token,
-                    secret_bytes_count,
-                    &old_iv[0],
-                    old_crypto_version,
-                    old_crypto_key_slot,
-                    &plain_token_length);
+                    encrypted_token, secret_bytes_count, &old_crypto_settings, &plain_token_length);
 
                 free(encrypted_token);
                 size_t encrypted_token_length;
                 encrypted_token = totp_crypto_encrypt(
                     plain_token,
                     plain_token_length,
-                    &plugin_state->iv[0],
-                    plugin_state->crypto_version,
-                    plugin_state->crypto_key_slot,
+                    &plugin_state->crypto_settings,
                     &encrypted_token_length);
 
                 memset_s(plain_token, plain_token_length, 0, plain_token_length);
@@ -700,12 +697,12 @@ bool totp_config_file_ensure_latest_encryption(
     const uint8_t* pin,
     uint8_t pin_length) {
     bool result = true;
-    if(plugin_state->crypto_version < CRYPTO_LATEST_VERSION) {
+    if(plugin_state->crypto_settings.crypto_version < CRYPTO_LATEST_VERSION) {
         FURI_LOG_I(LOGGING_TAG, "Migration to crypto v%d is needed", CRYPTO_LATEST_VERSION);
         char* backup_path = totp_config_file_backup(plugin_state);
         if(backup_path != NULL) {
             free(backup_path);
-            uint8_t crypto_key_slot = plugin_state->crypto_key_slot;
+            uint8_t crypto_key_slot = plugin_state->crypto_settings.crypto_key_slot;
             if(!totp_crypto_check_key_slot(crypto_key_slot)) {
                 crypto_key_slot = DEFAULT_CRYPTO_KEY_SLOT;
             }

+ 5 - 12
services/config/token_info_iterator.c

@@ -4,6 +4,7 @@
 #include <flipper_format/flipper_format_stream.h>
 #include <toolbox/stream/file_stream.h>
 #include "../../types/common.h"
+#include "../../types/crypto_settings.h"
 
 #define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part"
 #define STREAM_COPY_BUFFER_SIZE 128
@@ -15,9 +16,7 @@ struct TokenInfoIteratorContext {
     size_t last_seek_index;
     TokenInfo* current_token;
     FlipperFormat* config_file;
-    uint8_t* iv;
-    uint8_t crypto_version;
-    uint8_t crypto_key_slot;
+    CryptoSettings* crypto_settings;
     Storage* storage;
 };
 
@@ -242,9 +241,7 @@ static bool
 TokenInfoIteratorContext* totp_token_info_iterator_alloc(
     Storage* storage,
     FlipperFormat* config_file,
-    uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t crypto_key_slot) {
+    CryptoSettings* crypto_settings) {
     Stream* stream = flipper_format_get_raw_stream(config_file);
     stream_rewind(stream);
     size_t tokens_count = 0;
@@ -262,9 +259,7 @@ TokenInfoIteratorContext* totp_token_info_iterator_alloc(
     context->total_count = tokens_count;
     context->current_token = token_info_alloc();
     context->config_file = config_file;
-    context->iv = iv;
-    context->crypto_version = crypto_version;
-    context->crypto_key_slot = crypto_key_slot;
+    context->crypto_settings = crypto_settings;
     context->storage = storage;
     return context;
 }
@@ -461,9 +456,7 @@ bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t to
                    furi_string_get_cstr(temp_str),
                    furi_string_size(temp_str),
                    PlainTokenSecretEncodingBase32,
-                   context->iv,
-                   context->crypto_version,
-                   context->crypto_key_slot)) {
+                   context->crypto_settings)) {
                 FURI_LOG_W(
                     LOGGING_TAG,
                     "Token \"%s\" has plain secret",

+ 2 - 6
services/config/token_info_iterator.h

@@ -28,17 +28,13 @@ enum TotpIteratorUpdateTokenResults {
  * @brief Initializes a new token info iterator
  * @param storage storage reference
  * @param config_file config file to use
- * @param iv initialization vector (IV) to be used for encryption\decryption
- * @param crypto_version crypto algorithm version to be used
- * @param crypto_key_slot crypto key slot to be used
+ * @param crypto_settings crypto settings
  * @return Token info iterator context
  */
 TokenInfoIteratorContext* totp_token_info_iterator_alloc(
     Storage* storage,
     FlipperFormat* config_file,
-    uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t crypto_key_slot);
+    CryptoSettings* crypto_settings);
 
 /**
  * @brief Navigates iterator to the token with given index

+ 22 - 24
services/crypto/crypto_facade.c

@@ -1,5 +1,6 @@
 #include "crypto_facade.h"
 #include <furi_hal_crypto.h>
+#include <furi/core/check.h>
 #include "crypto_v1.h"
 #include "crypto_v2.h"
 #include "constants.h"
@@ -18,17 +19,16 @@ bool totp_crypto_check_key_slot(uint8_t key_slot) {
 uint8_t* totp_crypto_encrypt(
     const uint8_t* plain_data,
     const size_t plain_data_length,
-    const uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* encrypted_data_length) {
-    if(crypto_version == 1) {
-        return totp_crypto_encrypt_v1(plain_data, plain_data_length, iv, encrypted_data_length);
+    if(crypto_settings->crypto_version == 1) {
+        return totp_crypto_encrypt_v1(
+            plain_data, plain_data_length, crypto_settings, encrypted_data_length);
     }
 
-    if(crypto_version == 2) {
+    if(crypto_settings->crypto_version == 2) {
         return totp_crypto_encrypt_v2(
-            plain_data, plain_data_length, iv, key_slot, encrypted_data_length);
+            plain_data, plain_data_length, crypto_settings, encrypted_data_length);
     }
 
     furi_crash("Unsupported crypto version");
@@ -37,43 +37,41 @@ uint8_t* totp_crypto_encrypt(
 uint8_t* totp_crypto_decrypt(
     const uint8_t* encrypted_data,
     const size_t encrypted_data_length,
-    const uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* decrypted_data_length) {
-    if(crypto_version == 1) {
+    if(crypto_settings->crypto_version == 1) {
         return totp_crypto_decrypt_v1(
-            encrypted_data, encrypted_data_length, iv, decrypted_data_length);
+            encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length);
     }
 
-    if(crypto_version == 2) {
+    if(crypto_settings->crypto_version == 2) {
         return totp_crypto_decrypt_v2(
-            encrypted_data, encrypted_data_length, iv, key_slot, decrypted_data_length);
+            encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length);
     }
 
     furi_crash("Unsupported crypto version");
 }
 
 CryptoSeedIVResult
-    totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
-    if(plugin_state->crypto_version == 1) {
-        return totp_crypto_seed_iv_v1(plugin_state, pin, pin_length);
+    totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length) {
+    if(crypto_settings->crypto_version == 1) {
+        return totp_crypto_seed_iv_v1(crypto_settings, pin, pin_length);
     }
 
-    if(plugin_state->crypto_version == 2) {
-        return totp_crypto_seed_iv_v2(plugin_state, pin, pin_length);
+    if(crypto_settings->crypto_version == 2) {
+        return totp_crypto_seed_iv_v2(crypto_settings, pin, pin_length);
     }
 
     furi_crash("Unsupported crypto version");
 }
 
-bool totp_crypto_verify_key(const PluginState* plugin_state) {
-    if(plugin_state->crypto_version == 1) {
-        return totp_crypto_verify_key_v1(plugin_state);
+bool totp_crypto_verify_key(const CryptoSettings* crypto_settings) {
+    if(crypto_settings->crypto_version == 1) {
+        return totp_crypto_verify_key_v1(crypto_settings);
     }
 
-    if(plugin_state->crypto_version == 2) {
-        return totp_crypto_verify_key_v2(plugin_state);
+    if(crypto_settings->crypto_version == 2) {
+        return totp_crypto_verify_key_v2(crypto_settings);
     }
 
     furi_crash("Unsupported crypto version");

+ 12 - 18
services/crypto/crypto_facade.h

@@ -1,6 +1,9 @@
 #pragma once
 
-#include "../../types/plugin_state.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include "../../types/crypto_settings.h"
 #include "common_types.h"
 
 /**
@@ -14,52 +17,43 @@ bool totp_crypto_check_key_slot(uint8_t key_slot);
  * @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
  * @param plain_data plain data to be encrypted
  * @param plain_data_length plain data length
- * @param iv initialization vector (IV) to be used to encrypt plain data
- * @param crypto_version version of crypto algorithms to use
- * @param key_slot key slot to be used
+ * @param crypto_settings crypto settings
  * @param[out] encrypted_data_length encrypted data length
  * @return Encrypted data
  */
 uint8_t* totp_crypto_encrypt(
     const uint8_t* plain_data,
     const size_t plain_data_length,
-    const uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* encrypted_data_length);
 
 /**
  * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV)
  * @param encrypted_data encrypted data to be decrypted
  * @param encrypted_data_length encrypted data length
- * @param iv initialization vector (IV) to be used to encrypt plain data
- * @param crypto_version version of crypto algorithms to use
- * @param key_slot key slot to be used
+ * @param crypto_settings crypto settings
  * @param[out] decrypted_data_length decrypted data length
  * @return Decrypted data
  */
 uint8_t* totp_crypto_decrypt(
     const uint8_t* encrypted_data,
     const size_t encrypted_data_length,
-    const uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* decrypted_data_length);
 
 /**
  * @brief Seed initialization vector (IV) using user's PIN
- * @param plugin_state application state
- * @param key_slot key slot to be used
+ * @param crypto_settings crypto settings
  * @param pin user's PIN
  * @param pin_length user's PIN length
  * @return Results of seeding IV
  */
 CryptoSeedIVResult
-    totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
+    totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length);
 
 /**
  * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
- * @param plugin_state application state
+ * @param crypto_settings crypto settings
  * @return \c true if cryptographic information is valid; \c false otherwise
  */
-bool totp_crypto_verify_key(const PluginState* plugin_state);
+bool totp_crypto_verify_key(const CryptoSettings* crypto_settings);

+ 28 - 24
services/crypto/crypto_v1.c

@@ -1,4 +1,6 @@
 #include "crypto_v1.h"
+#include <stdlib.h>
+#include <furi.h>
 #include <furi_hal_crypto.h>
 #include <furi_hal_random.h>
 #include <furi_hal_version.h>
@@ -15,7 +17,7 @@ static const char* CRYPTO_VERIFY_KEY = "FFF_Crypto_pass";
 uint8_t* totp_crypto_encrypt_v1(
     const uint8_t* plain_data,
     const size_t plain_data_length,
-    const uint8_t* iv,
+    const CryptoSettings* crypto_settings,
     size_t* encrypted_data_length) {
     uint8_t* encrypted_data;
     size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR;
@@ -30,7 +32,7 @@ uint8_t* totp_crypto_encrypt_v1(
         furi_check(encrypted_data != NULL);
         *encrypted_data_length = plain_data_aligned_length;
 
-        furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
+        furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv);
         furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length);
         furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
 
@@ -41,7 +43,7 @@ uint8_t* totp_crypto_encrypt_v1(
         furi_check(encrypted_data != NULL);
         *encrypted_data_length = plain_data_length;
 
-        furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
+        furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv);
         furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length);
         furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
     }
@@ -52,26 +54,28 @@ uint8_t* totp_crypto_encrypt_v1(
 uint8_t* totp_crypto_decrypt_v1(
     const uint8_t* encrypted_data,
     const size_t encrypted_data_length,
-    const uint8_t* iv,
+    const CryptoSettings* crypto_settings,
     size_t* decrypted_data_length) {
     *decrypted_data_length = encrypted_data_length;
     uint8_t* decrypted_data = malloc(*decrypted_data_length);
     furi_check(decrypted_data != NULL);
-    furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
+    furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv);
     furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length);
     furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
     return decrypted_data;
 }
 
-CryptoSeedIVResult
-    totp_crypto_seed_iv_v1(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
+CryptoSeedIVResult totp_crypto_seed_iv_v1(
+    CryptoSettings* crypto_settings,
+    const uint8_t* pin,
+    uint8_t pin_length) {
     CryptoSeedIVResult result;
-    if(plugin_state->crypto_verify_data == NULL) {
+    if(crypto_settings->crypto_verify_data == NULL) {
         FURI_LOG_I(LOGGING_TAG, "Generating new IV");
-        furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE);
+        furi_hal_random_fill_buf(&crypto_settings->base_iv[0], TOTP_IV_SIZE);
     }
 
-    memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE);
+    memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], TOTP_IV_SIZE);
     if(pin != NULL && pin_length > 0) {
         uint8_t max_i;
         if(pin_length > TOTP_IV_SIZE) {
@@ -81,7 +85,7 @@ CryptoSeedIVResult
         }
 
         for(uint8_t i = 0; i < max_i; i++) {
-            plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1));
+            crypto_settings->iv[i] = crypto_settings->iv[i] ^ (uint8_t)(pin[i] * (i + 1));
         }
     } else {
         uint8_t max_i;
@@ -94,24 +98,24 @@ CryptoSeedIVResult
 
         const uint8_t* uid = (const uint8_t*)UID64_BASE; //-V566
         for(uint8_t i = 0; i < max_i; i++) {
-            plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i];
+            crypto_settings->iv[i] = crypto_settings->iv[i] ^ uid[i];
         }
     }
 
     result = CryptoSeedIVResultFlagSuccess;
-    if(plugin_state->crypto_verify_data == NULL) {
+    if(crypto_settings->crypto_verify_data == NULL) {
         FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data");
-        plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
-        furi_check(plugin_state->crypto_verify_data != NULL);
-        plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH;
+        crypto_settings->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
+        furi_check(crypto_settings->crypto_verify_data != NULL);
+        crypto_settings->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH;
 
-        plugin_state->crypto_verify_data = totp_crypto_encrypt_v1(
+        crypto_settings->crypto_verify_data = totp_crypto_encrypt_v1(
             (const uint8_t*)CRYPTO_VERIFY_KEY,
             CRYPTO_VERIFY_KEY_LENGTH,
-            &plugin_state->iv[0],
-            &plugin_state->crypto_verify_data_length);
+            crypto_settings,
+            &crypto_settings->crypto_verify_data_length);
 
-        plugin_state->pin_set = pin != NULL && pin_length > 0;
+        crypto_settings->pin_required = pin != NULL && pin_length > 0;
 
         result |= CryptoSeedIVResultFlagNewCryptoVerifyData;
     }
@@ -119,12 +123,12 @@ CryptoSeedIVResult
     return result;
 }
 
-bool totp_crypto_verify_key_v1(const PluginState* plugin_state) {
+bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings) {
     size_t decrypted_key_length;
     uint8_t* decrypted_key = totp_crypto_decrypt_v1(
-        plugin_state->crypto_verify_data,
-        plugin_state->crypto_verify_data_length,
-        &plugin_state->iv[0],
+        crypto_settings->crypto_verify_data,
+        crypto_settings->crypto_verify_data_length,
+        crypto_settings,
         &decrypted_key_length);
 
     bool key_valid = true;

+ 12 - 9
services/crypto/crypto_v1.h

@@ -1,49 +1,52 @@
 #pragma once
 
-#include "../../types/plugin_state.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "../../types/crypto_settings.h"
 #include "common_types.h"
 
 /**
  * @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
  * @param plain_data plain data to be encrypted
  * @param plain_data_length plain data length
- * @param iv initialization vector (IV) to be used to encrypt plain data
+ * @param crypto_settings crypto settings
  * @param[out] encrypted_data_length encrypted data length
  * @return Encrypted data
  */
 uint8_t* totp_crypto_encrypt_v1(
     const uint8_t* plain_data,
     const size_t plain_data_length,
-    const uint8_t* iv,
+    const CryptoSettings* crypto_settings,
     size_t* encrypted_data_length);
 
 /**
  * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV)
  * @param encrypted_data encrypted data to be decrypted
  * @param encrypted_data_length encrypted data length
- * @param iv initialization vector (IV) to be used to encrypt plain data
+ * @param crypto_settings crypto settings
  * @param[out] decrypted_data_length decrypted data length
  * @return Decrypted data
  */
 uint8_t* totp_crypto_decrypt_v1(
     const uint8_t* encrypted_data,
     const size_t encrypted_data_length,
-    const uint8_t* iv,
+    const CryptoSettings* crypto_settings,
     size_t* decrypted_data_length);
 
 /**
  * @brief Seed initialization vector (IV) using user's PIN
- * @param plugin_state application state
+ * @param crypto_settings crypto settings
  * @param pin user's PIN
  * @param pin_length user's PIN length
  * @return Results of seeding IV
  */
 CryptoSeedIVResult
-    totp_crypto_seed_iv_v1(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
+    totp_crypto_seed_iv_v1(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length);
 
 /**
  * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
- * @param plugin_state application state
+ * @param crypto_settings crypto settings
  * @return \c true if cryptographic information is valid; \c false otherwise
  */
-bool totp_crypto_verify_key_v1(const PluginState* plugin_state);
+bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings);

+ 39 - 31
services/crypto/crypto_v2.c

@@ -1,4 +1,6 @@
 #include "crypto_v2.h"
+#include <stdlib.h>
+#include <furi.h>
 #include <furi_hal_crypto.h>
 #include <furi_hal_random.h>
 #include <furi_hal_version.h>
@@ -28,8 +30,7 @@ static uint8_t get_crypto_verify_key_length() {
 uint8_t* totp_crypto_encrypt_v2(
     const uint8_t* plain_data,
     const size_t plain_data_length,
-    const uint8_t* iv,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* encrypted_data_length) {
     uint8_t* encrypted_data;
     size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR;
@@ -45,12 +46,14 @@ uint8_t* totp_crypto_encrypt_v2(
         *encrypted_data_length = plain_data_aligned_length;
 
         furi_check(
-            furi_hal_crypto_store_load_key(key_slot, iv), "Encryption failed: store_load_key");
+            furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv),
+            "Encryption failed: store_load_key");
         furi_check(
             furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length),
             "Encryption failed: encrypt");
         furi_check(
-            furi_hal_crypto_store_unload_key(key_slot), "Encryption failed: store_unload_key");
+            furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot),
+            "Encryption failed: store_unload_key");
 
         memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length);
         free(plain_data_aligned);
@@ -60,12 +63,14 @@ uint8_t* totp_crypto_encrypt_v2(
         *encrypted_data_length = plain_data_length;
 
         furi_check(
-            furi_hal_crypto_store_load_key(key_slot, iv), "Encryption failed: store_load_key");
+            furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv),
+            "Encryption failed: store_load_key");
         furi_check(
             furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length),
             "Encryption failed: encrypt");
         furi_check(
-            furi_hal_crypto_store_unload_key(key_slot), "Encryption failed: store_unload_key");
+            furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot),
+            "Encryption failed: store_unload_key");
     }
 
     return encrypted_data;
@@ -74,29 +79,34 @@ uint8_t* totp_crypto_encrypt_v2(
 uint8_t* totp_crypto_decrypt_v2(
     const uint8_t* encrypted_data,
     const size_t encrypted_data_length,
-    const uint8_t* iv,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* decrypted_data_length) {
     *decrypted_data_length = encrypted_data_length;
     uint8_t* decrypted_data = malloc(*decrypted_data_length);
     furi_check(decrypted_data != NULL);
-    furi_check(furi_hal_crypto_store_load_key(key_slot, iv), "Decryption failed: store_load_key");
+    furi_check(
+        furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv),
+        "Decryption failed: store_load_key");
     furi_check(
         furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length),
         "Decryption failed: decrypt");
-    furi_check(furi_hal_crypto_store_unload_key(key_slot), "Decryption failed: store_unload_key");
+    furi_check(
+        furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot),
+        "Decryption failed: store_unload_key");
     return decrypted_data;
 }
 
-CryptoSeedIVResult
-    totp_crypto_seed_iv_v2(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
+CryptoSeedIVResult totp_crypto_seed_iv_v2(
+    CryptoSettings* crypto_settings,
+    const uint8_t* pin,
+    uint8_t pin_length) {
     CryptoSeedIVResult result;
-    if(plugin_state->crypto_verify_data == NULL) {
+    if(crypto_settings->crypto_verify_data == NULL) {
         FURI_LOG_I(LOGGING_TAG, "Generating new IV");
-        furi_hal_random_fill_buf(&plugin_state->base_iv[0], CRYPTO_IV_LENGTH);
+        furi_hal_random_fill_buf(&crypto_settings->base_iv[0], CRYPTO_IV_LENGTH);
     }
 
-    memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], CRYPTO_IV_LENGTH);
+    memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH);
 
     const uint8_t* device_uid = get_device_uid();
     uint8_t device_uid_length = get_device_uid_length();
@@ -117,7 +127,7 @@ CryptoSeedIVResult
 
     uint8_t hmac[HMAC_SHA512_RESULT_SIZE] = {0};
     int hmac_result_code = hmac_sha512(
-        hmac_key, hmac_key_length, &plugin_state->base_iv[0], CRYPTO_IV_LENGTH, &hmac[0]);
+        hmac_key, hmac_key_length, &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH, &hmac[0]);
 
     memset_s(hmac_key, hmac_key_length, 0, hmac_key_length);
     free(hmac_key);
@@ -125,25 +135,24 @@ CryptoSeedIVResult
     if(hmac_result_code == 0) {
         uint8_t offset =
             hmac[HMAC_SHA512_RESULT_SIZE - 1] % (HMAC_SHA512_RESULT_SIZE - CRYPTO_IV_LENGTH - 1);
-        memcpy(&plugin_state->iv[0], &hmac[offset], CRYPTO_IV_LENGTH);
+        memcpy(&crypto_settings->iv[0], &hmac[offset], CRYPTO_IV_LENGTH);
 
         result = CryptoSeedIVResultFlagSuccess;
-        if(plugin_state->crypto_verify_data == NULL) {
+        if(crypto_settings->crypto_verify_data == NULL) {
             const uint8_t* crypto_vkey = get_crypto_verify_key();
             uint8_t crypto_vkey_length = get_crypto_verify_key_length();
             FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data");
-            plugin_state->crypto_verify_data = malloc(crypto_vkey_length);
-            furi_check(plugin_state->crypto_verify_data != NULL);
-            plugin_state->crypto_verify_data_length = crypto_vkey_length;
+            crypto_settings->crypto_verify_data = malloc(crypto_vkey_length);
+            furi_check(crypto_settings->crypto_verify_data != NULL);
+            crypto_settings->crypto_verify_data_length = crypto_vkey_length;
 
-            plugin_state->crypto_verify_data = totp_crypto_encrypt_v2(
+            crypto_settings->crypto_verify_data = totp_crypto_encrypt_v2(
                 crypto_vkey,
                 crypto_vkey_length,
-                &plugin_state->iv[0],
-                plugin_state->crypto_key_slot,
-                &plugin_state->crypto_verify_data_length);
+                crypto_settings,
+                &crypto_settings->crypto_verify_data_length);
 
-            plugin_state->pin_set = pin != NULL && pin_length > 0;
+            crypto_settings->pin_required = pin != NULL && pin_length > 0;
 
             result |= CryptoSeedIVResultFlagNewCryptoVerifyData;
         }
@@ -154,13 +163,12 @@ CryptoSeedIVResult
     return result;
 }
 
-bool totp_crypto_verify_key_v2(const PluginState* plugin_state) {
+bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings) {
     size_t decrypted_key_length;
     uint8_t* decrypted_key = totp_crypto_decrypt_v2(
-        plugin_state->crypto_verify_data,
-        plugin_state->crypto_verify_data_length,
-        &plugin_state->iv[0],
-        plugin_state->crypto_key_slot,
+        crypto_settings->crypto_verify_data,
+        crypto_settings->crypto_verify_data_length,
+        crypto_settings,
         &decrypted_key_length);
 
     const uint8_t* crypto_vkey = get_crypto_verify_key();

+ 12 - 14
services/crypto/crypto_v2.h

@@ -1,54 +1,52 @@
 #pragma once
 
-#include "../../types/plugin_state.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "../../types/crypto_settings.h"
 #include "common_types.h"
 
 /**
  * @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
  * @param plain_data plain data to be encrypted
  * @param plain_data_length plain data length
- * @param iv initialization vector (IV) to be used to encrypt plain data
- * @param key_slot key slot to be used
+ * @param crypto_settings crypto settings
  * @param[out] encrypted_data_length encrypted data length
  * @return Encrypted data
  */
 uint8_t* totp_crypto_encrypt_v2(
     const uint8_t* plain_data,
     const size_t plain_data_length,
-    const uint8_t* iv,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* encrypted_data_length);
 
 /**
  * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV)
  * @param encrypted_data encrypted data to be decrypted
  * @param encrypted_data_length encrypted data length
- * @param iv initialization vector (IV) to be used to encrypt plain data
- * @param key_slot key slot to be used
+ * @param crypto_settings crypto settings
  * @param[out] decrypted_data_length decrypted data length
  * @return Decrypted data
  */
 uint8_t* totp_crypto_decrypt_v2(
     const uint8_t* encrypted_data,
     const size_t encrypted_data_length,
-    const uint8_t* iv,
-    uint8_t key_slot,
+    const CryptoSettings* crypto_settings,
     size_t* decrypted_data_length);
 
 /**
  * @brief Seed initialization vector (IV) using user's PIN
- * @param plugin_state application state
- * @param key_slot key slot to be used
+ * @param crypto_settings crypto settings
  * @param pin user's PIN
  * @param pin_length user's PIN length
  * @return Results of seeding IV
  */
 CryptoSeedIVResult
-    totp_crypto_seed_iv_v2(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
+    totp_crypto_seed_iv_v2(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length);
 
 /**
  * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
- * @param plugin_state application state
+ * @param crypto_settings crypto settings
  * @return \c true if cryptographic information is valid; \c false otherwise
  */
-bool totp_crypto_verify_key_v2(const PluginState* plugin_state);
+bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings);

+ 89 - 59
totp_app.c

@@ -17,6 +17,7 @@
 #include "ui/common_dialogs.h"
 #include "services/crypto/crypto_facade.h"
 #include "cli/cli.h"
+#include "version.h"
 
 struct TotpRenderCallbackContext {
     FuriMutex* mutex;
@@ -39,46 +40,28 @@ static void input_callback(InputEvent* const input_event, void* const ctx) {
     furi_message_queue_put(event_queue, &event, FuriWaitForever);
 }
 
-static bool totp_activate_initial_scene(PluginState* const plugin_state) {
-    if(plugin_state->crypto_verify_data == NULL) {
-        DialogMessage* message = dialog_message_alloc();
-        dialog_message_set_buttons(message, "No", NULL, "Yes");
-        dialog_message_set_text(
-            message,
-            "Would you like to setup PIN?",
-            SCREEN_WIDTH_CENTER,
-            SCREEN_HEIGHT_CENTER,
-            AlignCenter,
-            AlignCenter);
-        DialogMessageButton dialog_result =
-            dialog_message_show(plugin_state->dialogs_app, message);
-        dialog_message_free(message);
-        if(!totp_crypto_check_key_slot(plugin_state->crypto_key_slot)) {
-            totp_dialogs_config_loading_error(plugin_state);
-            return false;
-        }
-
-        if(dialog_result == DialogMessageButtonRight) {
-            totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
-        } else {
-            CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0);
-            if(seed_result & CryptoSeedIVResultFlagSuccess &&
-               seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
-                if(!totp_config_file_update_crypto_signatures(plugin_state)) {
-                    totp_dialogs_config_loading_error(plugin_state);
-                    return false;
-                }
-            } else if(seed_result == CryptoSeedIVResultFailed) {
-                totp_dialogs_config_loading_error(plugin_state);
-                return false;
-            }
+static bool first_run_init(PluginState* const plugin_state) {
+    DialogMessage* message = dialog_message_alloc();
+    dialog_message_set_buttons(message, "No", NULL, "Yes");
+    dialog_message_set_text(
+        message,
+        "Would you like to setup PIN?",
+        SCREEN_WIDTH_CENTER,
+        SCREEN_HEIGHT_CENTER,
+        AlignCenter,
+        AlignCenter);
+    DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs_app, message);
+    dialog_message_free(message);
+    if(!totp_crypto_check_key_slot(plugin_state->crypto_settings.crypto_key_slot)) {
+        totp_dialogs_config_loading_error(plugin_state);
+        return false;
+    }
 
-            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
-        }
-    } else if(plugin_state->pin_set) {
+    if(dialog_result == DialogMessageButtonRight) {
         totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
     } else {
-        CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0);
+        CryptoSeedIVResult seed_result =
+            totp_crypto_seed_iv(&plugin_state->crypto_settings, NULL, 0);
         if(seed_result & CryptoSeedIVResultFlagSuccess &&
            seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
             if(!totp_config_file_update_crypto_signatures(plugin_state)) {
@@ -90,24 +73,65 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) {
             return false;
         }
 
-        if(totp_crypto_verify_key(plugin_state)) {
-            totp_config_file_ensure_latest_encryption(plugin_state, NULL, 0);
-            totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
-        } else {
-            FURI_LOG_E(
-                LOGGING_TAG,
-                "Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other");
-            DialogMessage* message = dialog_message_alloc();
-            dialog_message_set_buttons(message, "Exit", NULL, NULL);
-            dialog_message_set_text(
-                message,
-                "Digital signature verification failed",
-                SCREEN_WIDTH_CENTER,
-                SCREEN_HEIGHT_CENTER,
-                AlignCenter,
-                AlignCenter);
-            dialog_message_show(plugin_state->dialogs_app, message);
-            dialog_message_free(message);
+        totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+    }
+
+    return true;
+}
+
+static bool pinless_activation(PluginState* const plugin_state) {
+    CryptoSeedIVResult seed_result = totp_crypto_seed_iv(&plugin_state->crypto_settings, NULL, 0);
+    if(seed_result & CryptoSeedIVResultFlagSuccess &&
+       seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) {
+        if(!totp_config_file_update_crypto_signatures(plugin_state)) {
+            totp_dialogs_config_loading_error(plugin_state);
+            return false;
+        }
+    } else if(seed_result == CryptoSeedIVResultFailed) {
+        totp_dialogs_config_loading_error(plugin_state);
+        return false;
+    }
+
+    if(totp_crypto_verify_key(&plugin_state->crypto_settings)) {
+        totp_config_file_ensure_latest_encryption(plugin_state, NULL, 0);
+        totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
+    } else {
+        FURI_LOG_E(
+            LOGGING_TAG,
+            "Digital signature verification failed. Looks like conf file was created on another device and can't be used on any other");
+        DialogMessage* message = dialog_message_alloc();
+        dialog_message_set_buttons(message, "Exit", NULL, NULL);
+        dialog_message_set_text(
+            message,
+            "Digital signature verification failed",
+            SCREEN_WIDTH_CENTER,
+            SCREEN_HEIGHT_CENTER,
+            AlignCenter,
+            AlignCenter);
+        dialog_message_show(plugin_state->dialogs_app, message);
+        dialog_message_free(message);
+        return false;
+    }
+
+    return true;
+}
+
+static bool pin_activation(PluginState* const plugin_state) {
+    totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication);
+    return true;
+}
+
+static bool totp_activate_initial_scene(PluginState* const plugin_state) {
+    if(plugin_state->crypto_settings.crypto_verify_data == NULL) {
+        if(!first_run_init(plugin_state)) {
+            return false;
+        }
+    } else if(plugin_state->crypto_settings.pin_required) {
+        if(!pin_activation(plugin_state)) {
+            return false;
+        }
+    } else {
+        if(!pinless_activation(plugin_state)) {
             return false;
         }
     }
@@ -130,7 +154,7 @@ static bool on_user_idle(void* context) {
 static bool totp_plugin_state_init(PluginState* const plugin_state) {
     plugin_state->gui = furi_record_open(RECORD_GUI);
     plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
-    memset(&plugin_state->iv[0], 0, CRYPTO_IV_LENGTH);
+    memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH);
 
     if(!totp_config_file_load(plugin_state)) {
         totp_dialogs_config_loading_error(plugin_state);
@@ -147,7 +171,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
     }
 #endif
 
-    if(plugin_state->pin_set) {
+    if(plugin_state->crypto_settings.pin_required) {
         plugin_state->idle_timeout_context =
             idle_timeout_alloc(TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC, &on_user_idle, plugin_state);
         idle_timeout_start(plugin_state->idle_timeout_context);
@@ -169,8 +193,8 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
 
     totp_config_file_close(plugin_state);
 
-    if(plugin_state->crypto_verify_data != NULL) {
-        free(plugin_state->crypto_verify_data);
+    if(plugin_state->crypto_settings.crypto_verify_data != NULL) {
+        free(plugin_state->crypto_settings.crypto_verify_data);
     }
 
 #ifdef TOTP_BADBT_AUTOMATION_ENABLED
@@ -188,6 +212,12 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
 }
 
 int32_t totp_app() {
+    FURI_LOG_I(
+        LOGGING_TAG,
+        "App version: %" PRIu8 ".%" PRIu8 ".%" PRIu8,
+        TOTP_APP_VERSION_MAJOR,
+        TOTP_APP_VERSION_MINOR,
+        TOTP_APP_VERSION_PATCH);
     PluginState* plugin_state = malloc(sizeof(PluginState));
     furi_check(plugin_state != NULL);
 

+ 41 - 0
types/crypto_settings.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include <stdint.h>
+#include "../services/crypto/constants.h"
+
+typedef struct {
+    /**
+     * @brief Crypto key slot to be used
+     */
+    uint8_t crypto_key_slot;
+
+    /**
+     * @brief Crypto algorithms version to be used
+     */
+    uint8_t crypto_version;
+
+    /**
+     * @brief Initialization vector (IV) to be used for encryption\decryption 
+     */
+    uint8_t iv[CRYPTO_IV_LENGTH];
+
+    /**
+     * @brief Basic randomly-generated initialization vector (IV)
+     */
+    uint8_t base_iv[CRYPTO_IV_LENGTH];
+
+    /**
+     * @brief Encrypted well-known data
+     */
+    uint8_t* crypto_verify_data;
+
+    /**
+     * @brief Encrypted well-known data length
+     */
+    size_t crypto_verify_data_length;
+
+    /**
+     * @brief Whether user's PIN is required or not 
+     */
+    bool pin_required;
+} CryptoSettings;

+ 5 - 35
types/plugin_state.h

@@ -13,7 +13,7 @@
 #ifdef TOTP_BADBT_AUTOMATION_ENABLED
 #include "../workers/bt_type_code/bt_type_code.h"
 #endif
-#include "../services/crypto/constants.h"
+#include "crypto_settings.h"
 
 /**
  * @brief Application state structure
@@ -49,31 +49,6 @@ typedef struct {
      */
     ConfigFileContext* config_file_context;
 
-    /**
-     * @brief Encrypted well-known data
-     */
-    uint8_t* crypto_verify_data;
-
-    /**
-     * @brief Encrypted well-known data length
-     */
-    size_t crypto_verify_data_length;
-
-    /**
-     * @brief Whether PIN is set by user or not 
-     */
-    bool pin_set;
-
-    /**
-     * @brief Initialization vector (IV) to be used for encryption\decryption 
-     */
-    uint8_t iv[CRYPTO_IV_LENGTH];
-
-    /**
-     * @brief Basic randomly-generated initialization vector (IV)
-     */
-    uint8_t base_iv[CRYPTO_IV_LENGTH];
-
     /**
      * @brief Notification method
      */
@@ -107,17 +82,12 @@ typedef struct {
     uint8_t active_font_index;
 
     /**
-     * @brief Crypto key slot to be used
-     */
-    uint8_t crypto_key_slot;
-
-    /**
-     * @brief Crypto algorithms version to be used
+     * @brief Application even queue
      */
-    uint8_t crypto_version;
+    FuriMessageQueue* event_queue;
 
     /**
-     * @brief Application even queue
+     * @brief Crypto settings
      */
-    FuriMessageQueue* event_queue;
+    CryptoSettings crypto_settings;
 } PluginState;

+ 3 - 9
types/token_info.c

@@ -1,4 +1,5 @@
 #include "token_info.h"
+#include <furi/core/check.h>
 #include <base32.h>
 #include <base64.h>
 #include <memset_s.h>
@@ -25,9 +26,7 @@ bool token_info_set_secret(
     const char* plain_token_secret,
     size_t token_secret_length,
     PlainTokenSecretEncoding plain_token_secret_encoding,
-    const uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t crypto_key_slot) {
+    const CryptoSettings* crypto_settings) {
     if(token_secret_length == 0) return false;
     uint8_t* plain_secret;
     size_t plain_secret_length;
@@ -57,12 +56,7 @@ bool token_info_set_secret(
         }
 
         token_info->token = totp_crypto_encrypt(
-            plain_secret,
-            plain_secret_length,
-            iv,
-            crypto_version,
-            crypto_key_slot,
-            &token_info->token_length);
+            plain_secret, plain_secret_length, crypto_settings, &token_info->token_length);
         result = true;
     } else {
         result = false;

+ 4 - 7
types/token_info.h

@@ -1,8 +1,9 @@
 #pragma once
 
-#include <inttypes.h>
+#include <stdint.h>
 #include <stdbool.h>
 #include <furi/core/string.h>
+#include "crypto_settings.h"
 
 #define TOKEN_HASH_ALGO_SHA1_NAME "sha1"
 #define TOKEN_HASH_ALGO_STEAM_NAME "steam"
@@ -200,9 +201,7 @@ void token_info_free(TokenInfo* token_info);
  * @param plain_token_secret plain token secret
  * @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 crypto_version crypto algorithm version to be used
- * @param crypto_key_slot crypto key slot to be used
+ * @param crypto_settings crypto settings
  * @return \c true if token successfully set; \c false otherwise
  */
 bool token_info_set_secret(
@@ -210,9 +209,7 @@ bool token_info_set_secret(
     const char* plain_token_secret,
     size_t token_secret_length,
     PlainTokenSecretEncoding plain_token_secret_encoding,
-    const uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t crypto_key_slot);
+    const CryptoSettings* crypto_settings);
 
 /**
  * @brief Sets token digits count from \c uint8_t value

+ 3 - 10
ui/scenes/add_new_token/totp_scene_add_new_token.c

@@ -42,9 +42,7 @@ typedef struct {
 
 struct TotpAddContext {
     SceneState* scene_state;
-    uint8_t* iv;
-    uint8_t crypto_version;
-    uint8_t crypto_key_slot;
+    const CryptoSettings* crypto_settings;
 };
 
 enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 };
@@ -60,9 +58,7 @@ static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, con
            context_t->scene_state->token_secret,
            context_t->scene_state->token_secret_length,
            PlainTokenSecretEncodingBase32,
-           context_t->iv,
-           context_t->crypto_version,
-           context_t->crypto_key_slot)) {
+           context_t->crypto_settings)) {
         return TotpIteratorUpdateTokenResultInvalidSecret;
     }
 
@@ -275,10 +271,7 @@ bool totp_scene_add_new_token_handle_event(
             break;
         case ConfirmButton: {
             struct TotpAddContext add_context = {
-                .iv = plugin_state->iv,
-                .scene_state = scene_state,
-                .crypto_version = plugin_state->crypto_version,
-                .crypto_key_slot = plugin_state->crypto_key_slot};
+                .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(

+ 5 - 5
ui/scenes/authenticate/totp_scene_authenticate.c

@@ -24,7 +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, CRYPTO_IV_LENGTH);
+    memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH);
 }
 
 void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) {
@@ -35,7 +35,7 @@ void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_st
         v_shift = -10;
     }
 
-    if(plugin_state->crypto_verify_data == NULL) {
+    if(plugin_state->crypto_settings.crypto_verify_data == NULL) {
         canvas_draw_str_aligned(
             canvas,
             SCREEN_WIDTH_CENTER,
@@ -123,14 +123,14 @@ bool totp_scene_authenticate_handle_event(
         }
     } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) {
         CryptoSeedIVResult seed_result = totp_crypto_seed_iv(
-            plugin_state, &scene_state->code_input[0], scene_state->code_length);
+            &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)) {
+        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);
@@ -138,7 +138,7 @@ bool totp_scene_authenticate_handle_event(
         } else {
             FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid");
             memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH);
-            memset(&plugin_state->iv[0], 0, CRYPTO_IV_LENGTH);
+            memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH);
             scene_state->code_length = 0;
 
             DialogMessage* message = dialog_message_alloc();

+ 28 - 58
ui/scenes/generate_token/totp_scene_generate_token.c

@@ -33,8 +33,8 @@ typedef struct {
 typedef struct {
     char last_code[TokenDigitsCountMax + 1];
     TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context;
-    NotificationMessage const** notification_sequence_new_token;
-    NotificationMessage const** notification_sequence_automation;
+    NotificationMessage const* notification_sequence_new_token[8];
+    NotificationMessage const* notification_sequence_automation[11];
     FuriMutex* last_code_update_sync;
     TotpGenerateCodeWorkerContext* generate_code_worker_context;
     UiPrecalculatedDimensions ui_precalculated_dimensions;
@@ -44,40 +44,29 @@ typedef struct {
 
 static const NotificationSequence*
     get_notification_sequence_new_token(const PluginState* plugin_state, SceneState* scene_state) {
-    if(scene_state->notification_sequence_new_token == NULL) {
-        uint8_t i = 0;
-        uint8_t length = 4;
+    if(scene_state->notification_sequence_new_token[0] == NULL) {
+        NotificationMessage const** sequence = &scene_state->notification_sequence_new_token[0];
+        *(sequence++) = &message_display_backlight_on;
+        *(sequence++) = &message_green_255;
         if(plugin_state->notification_method & NotificationMethodVibro) {
-            length += 2;
+            *(sequence++) = &message_vibro_on;
         }
 
         if(plugin_state->notification_method & NotificationMethodSound) {
-            length += 2;
+            *(sequence++) = &message_note_c5;
         }
 
-        scene_state->notification_sequence_new_token = malloc(sizeof(void*) * length);
-        furi_check(scene_state->notification_sequence_new_token != NULL);
-        scene_state->notification_sequence_new_token[i++] = &message_display_backlight_on;
-        scene_state->notification_sequence_new_token[i++] = &message_green_255;
-        if(plugin_state->notification_method & NotificationMethodVibro) {
-            scene_state->notification_sequence_new_token[i++] = &message_vibro_on;
-        }
-
-        if(plugin_state->notification_method & NotificationMethodSound) {
-            scene_state->notification_sequence_new_token[i++] = &message_note_c5;
-        }
-
-        scene_state->notification_sequence_new_token[i++] = &message_delay_50;
+        *(sequence++) = &message_delay_50;
 
         if(plugin_state->notification_method & NotificationMethodVibro) {
-            scene_state->notification_sequence_new_token[i++] = &message_vibro_off;
+            *(sequence++) = &message_vibro_off;
         }
 
         if(plugin_state->notification_method & NotificationMethodSound) {
-            scene_state->notification_sequence_new_token[i++] = &message_sound_off;
+            *(sequence++) = &message_sound_off;
         }
 
-        scene_state->notification_sequence_new_token[i++] = NULL;
+        *(sequence++) = NULL;
     }
 
     return (NotificationSequence*)scene_state->notification_sequence_new_token;
@@ -85,44 +74,33 @@ static const NotificationSequence*
 
 static const NotificationSequence*
     get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) {
-    if(scene_state->notification_sequence_automation == NULL) {
-        uint8_t i = 0;
-        uint8_t length = 3;
-        if(plugin_state->notification_method & NotificationMethodVibro) {
-            length += 2;
-        }
+    if(scene_state->notification_sequence_automation[0] == NULL) {
+        NotificationMessage const** sequence = &scene_state->notification_sequence_automation[0];
 
-        if(plugin_state->notification_method & NotificationMethodSound) {
-            length += 6;
-        }
-
-        scene_state->notification_sequence_automation = malloc(sizeof(void*) * length);
-        furi_check(scene_state->notification_sequence_automation != NULL);
-
-        scene_state->notification_sequence_automation[i++] = &message_blue_255;
+        *(sequence++) = &message_blue_255;
         if(plugin_state->notification_method & NotificationMethodVibro) {
-            scene_state->notification_sequence_automation[i++] = &message_vibro_on;
+            *(sequence++) = &message_vibro_on;
         }
 
         if(plugin_state->notification_method & NotificationMethodSound) {
-            scene_state->notification_sequence_automation[i++] = &message_note_d5; //-V525
-            scene_state->notification_sequence_automation[i++] = &message_delay_50;
-            scene_state->notification_sequence_automation[i++] = &message_note_e4;
-            scene_state->notification_sequence_automation[i++] = &message_delay_50;
-            scene_state->notification_sequence_automation[i++] = &message_note_f3;
+            *(sequence++) = &message_note_d5; //-V525
+            *(sequence++) = &message_delay_50;
+            *(sequence++) = &message_note_e4;
+            *(sequence++) = &message_delay_50;
+            *(sequence++) = &message_note_f3;
         }
 
-        scene_state->notification_sequence_automation[i++] = &message_delay_50;
+        *(sequence++) = &message_delay_50;
 
         if(plugin_state->notification_method & NotificationMethodVibro) {
-            scene_state->notification_sequence_automation[i++] = &message_vibro_off;
+            *(sequence++) = &message_vibro_off;
         }
 
         if(plugin_state->notification_method & NotificationMethodSound) {
-            scene_state->notification_sequence_automation[i++] = &message_sound_off;
+            *(sequence++) = &message_sound_off;
         }
 
-        scene_state->notification_sequence_automation[i++] = NULL;
+        *(sequence++) = NULL;
     }
 
     return (NotificationSequence*)scene_state->notification_sequence_automation;
@@ -213,6 +191,8 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) {
 
     scene_state->active_font = available_fonts[plugin_state->active_font_index];
     scene_state->notification_app = furi_record_open(RECORD_NOTIFICATION);
+    scene_state->notification_sequence_automation[0] = NULL;
+    scene_state->notification_sequence_new_token[0] = NULL;
 
 #ifdef TOTP_BADBT_AUTOMATION_ENABLED
 
@@ -235,9 +215,7 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) {
         totp_token_info_iterator_get_current_token(iterator_context),
         scene_state->last_code_update_sync,
         plugin_state->timezone_offset,
-        plugin_state->iv,
-        plugin_state->crypto_version,
-        plugin_state->crypto_key_slot);
+        &plugin_state->crypto_settings);
 
     totp_generate_code_worker_set_code_generated_handler(
         scene_state->generate_code_worker_context, &on_new_token_code_generated, plugin_state);
@@ -448,14 +426,6 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
     }
 #endif
 
-    if(scene_state->notification_sequence_new_token != NULL) {
-        free(scene_state->notification_sequence_new_token);
-    }
-
-    if(scene_state->notification_sequence_automation != NULL) {
-        free(scene_state->notification_sequence_automation);
-    }
-
     furi_mutex_free(scene_state->last_code_update_sync);
 
     free(scene_state);

+ 5 - 0
version.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#define TOTP_APP_VERSION_MAJOR (3)
+#define TOTP_APP_VERSION_MINOR (2)
+#define TOTP_APP_VERSION_PATCH (0)

+ 5 - 15
workers/generate_totp_code/generate_totp_code.c

@@ -1,5 +1,6 @@
 #include "generate_totp_code.h"
 #include <furi/core/thread.h>
+#include <furi/core/check.h>
 #include "../../services/crypto/crypto_facade.h"
 #include "../../services/totp/totp.h"
 #include "../../services/convert/convert.h"
@@ -14,9 +15,7 @@ struct TotpGenerateCodeWorkerContext {
     FuriMutex* code_buffer_sync;
     const TokenInfo* token_info;
     float timezone_offset;
-    uint8_t* iv;
-    uint8_t crypto_version;
-    uint8_t crypto_key_slot;
+    const CryptoSettings* crypto_settings;
     TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler;
     void* on_new_code_generated_handler_context;
     TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler;
@@ -71,12 +70,7 @@ static void generate_totp_code(
     if(token_info->token != NULL && token_info->token_length > 0) {
         size_t key_length;
         uint8_t* key = totp_crypto_decrypt(
-            token_info->token,
-            token_info->token_length,
-            context->iv,
-            context->crypto_version,
-            context->crypto_key_slot,
-            &key_length);
+            token_info->token, token_info->token_length, context->crypto_settings, &key_length);
 
         int_token_to_str(
             totp_at(
@@ -154,18 +148,14 @@ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
     const TokenInfo* token_info,
     FuriMutex* code_buffer_sync,
     float timezone_offset,
-    uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t crypto_key_slot) {
+    const CryptoSettings* crypto_settings) {
     TotpGenerateCodeWorkerContext* context = malloc(sizeof(TotpGenerateCodeWorkerContext));
     furi_check(context != NULL);
     context->code_buffer = code_buffer;
     context->token_info = token_info;
     context->code_buffer_sync = code_buffer_sync;
     context->timezone_offset = timezone_offset;
-    context->iv = iv;
-    context->crypto_version = crypto_version;
-    context->crypto_key_slot = crypto_key_slot;
+    context->crypto_settings = crypto_settings;
     context->thread = furi_thread_alloc();
     furi_thread_set_name(context->thread, "TOTPGenerateWorker");
     furi_thread_set_stack_size(context->thread, 2048);

+ 2 - 5
workers/generate_totp_code/generate_totp_code.h

@@ -38,8 +38,7 @@ enum TotGenerateCodeWorkerEvents {
  * @param token_info token info to be used to generate code
  * @param code_buffer_sync code buffer synchronization primitive
  * @param timezone_offset timezone offset to be used to generate code
- * @param iv initialization vector (IV) to be used to decrypt token secret
- * @param crypto_key_slot crypto key slot to be used
+ * @param crypto_settings crypto settings
  * @return worker context
  */
 TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
@@ -47,9 +46,7 @@ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
     const TokenInfo* token_info,
     FuriMutex* code_buffer_sync,
     float timezone_offset,
-    uint8_t* iv,
-    uint8_t crypto_version,
-    uint8_t crypto_key_slot);
+    const CryptoSettings* crypto_settings);
 
 /**
  * @brief Stops generate code worker