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

+ 0 - 2
application.fam

@@ -11,9 +11,7 @@ App(
         "storage",
         "input", 
         "notification",
-#ifdef TOTP_BADBT_TYPE_ENABLED
         "bt"
-#endif
     ],
     stack_size=2 * 1024,
     order=20,

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

@@ -1,7 +1,7 @@
 #include "reset.h"
 
 #include <stdlib.h>
-#include <furi/furi.h>
+#include <furi/core/string.h>
 #include "../../cli_helpers.h"
 #include "../../../services/config/config.h"
 

+ 6 - 1
features_config.h

@@ -1,8 +1,12 @@
 // Include Bluetooth token input automation
+#ifndef TOTP_NO_BADBT_TYPE
 #define TOTP_BADBT_TYPE_ENABLED
+#endif
 
 // Include token input automation icons on the main screen
+#ifndef TOTP_NO_AUTOMATION_ICONS
 #define TOTP_AUTOMATION_ICONS_ENABLED
+#endif
 
 // List of compatible firmwares
 #define TOTP_FIRMWARE_OFFICIAL_STABLE (1)
@@ -10,7 +14,8 @@
 #define TOTP_FIRMWARE_XTREME (3)
 // End of list
 
-// Target firmware to build for
+// Target firmware to build for.
 #ifndef TOTP_TARGET_FIRMWARE
+// Defaulting to Xtreme if not previously defined
 #define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME
 #endif

+ 3 - 0
lib/polyfills/strnlen.h

@@ -1,3 +1,6 @@
+#pragma once
+#pragma weak strnlen
+
 #include <stddef.h>
 
 size_t strnlen(const char* s, size_t maxlen);

+ 1 - 1
services/config/config.c

@@ -57,7 +57,7 @@ static char* totp_config_file_backup_i(Storage* storage) {
     }
 
     if(backup_file_exists ||
-       !storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) == FSE_OK) {
+       storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) != FSE_OK) {
         FURI_LOG_E(LOGGING_TAG, "Unable to take a backup");
         free(backup_path);
         return NULL;

+ 0 - 1
services/config/config.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include <flipper_format/flipper_format.h>
-#include <furi.h>
 #include "../../types/plugin_state.h"
 #include "../../types/token_info.h"
 #include "constants.h"

+ 3 - 2
services/crypto/crypto.c

@@ -1,6 +1,7 @@
 #include "crypto.h"
-#include <furi.h>
-#include <furi_hal.h>
+#include <furi_hal_crypto.h>
+#include <furi_hal_random.h>
+#include <furi_hal_version.h>
 #include "../config/config.h"
 #include "../../types/common.h"
 #include "memset_s.h"

+ 1 - 3
services/totp/totp.c

@@ -1,15 +1,13 @@
 #include "totp.h"
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
-#include <string.h>
 #include <math.h>
+#include <timezone_utils.h>
 #include "../hmac/hmac_sha1.h"
 #include "../hmac/hmac_sha256.h"
 #include "../hmac/hmac_sha512.h"
 #include "../hmac/byteswap.h"
-#include "../../lib/timezone_utils/timezone_utils.h"
 
 #define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE
 

+ 0 - 7
totp_app.c

@@ -1,10 +1,7 @@
-#include <furi.h>
-#include <furi_hal.h>
 #include <gui/gui.h>
 #include <input/input.h>
 #include <dialogs/dialogs.h>
 #include <stdlib.h>
-#include <flipper_format/flipper_format.h>
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
 #include <dolphin/dolphin.h>
@@ -105,10 +102,6 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
     }
 
     plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if(plugin_state->mutex == NULL) {
-        FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n");
-        return false;
-    }
 
 #ifdef TOTP_BADBT_TYPE_ENABLED
     if(plugin_state->automation_method & AutomationMethodBadBt) {

+ 0 - 2
types/token_info.c

@@ -1,9 +1,7 @@
 #include "token_info.h"
-#include <furi_hal.h>
 #include <base32.h>
 #include <base64.h>
 #include <memset_s.h>
-#include <strnlen.h>
 #include "common.h"
 #include "../services/crypto/crypto.h"
 

+ 1 - 1
types/token_info.h

@@ -2,7 +2,7 @@
 
 #include <inttypes.h>
 #include <stdbool.h>
-#include <furi/furi.h>
+#include <furi/core/string.h>
 
 #define TOTP_TOKEN_DURATION_DEFAULT (30)
 

+ 0 - 1
ui/scenes/add_new_token/totp_input_text.c

@@ -1,6 +1,5 @@
 #include "totp_input_text.h"
 #include <gui/view_i.h>
-#include "../../../lib/polyfills/strnlen.h"
 
 void view_draw(View* view, Canvas* canvas) {
     furi_assert(view);

+ 0 - 2
ui/scenes/add_new_token/totp_scene_add_new_token.h

@@ -1,8 +1,6 @@
 #pragma once
 
 #include <gui/gui.h>
-#include <furi.h>
-#include <furi_hal.h>
 #include "../../../types/plugin_state.h"
 #include "../../../types/plugin_event.h"
 

+ 69 - 127
ui/scenes/generate_token/totp_scene_generate_token.c

@@ -2,19 +2,16 @@
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
 #include <totp_icons.h>
+#include <roll_value.h>
 #include "totp_scene_generate_token.h"
 #include "../../../types/token_info.h"
 #include "../../../types/common.h"
 #include "../../constants.h"
-#include "../../../services/totp/totp.h"
 #include "../../../services/config/config.h"
-#include "../../../services/crypto/crypto.h"
-#include "../../../services/convert/convert.h"
-#include "../../../lib/polyfills/memset_s.h"
-#include "../../../lib/roll_value/roll_value.h"
 #include "../../scene_director.h"
 #include "../token_menu/totp_scene_token_menu.h"
 #include "../../../features_config.h"
+#include "../../../workers/generate_totp_code/generate_totp_code.h"
 #include "../../../workers/usb_type_code/usb_type_code.h"
 #ifdef TOTP_BADBT_TYPE_ENABLED
 #include "../../../workers/bt_type_code/bt_type_code.h"
@@ -23,18 +20,18 @@
 
 #define PROGRESS_BAR_MARGIN (3)
 #define PROGRESS_BAR_HEIGHT (4)
-static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
 
 typedef struct {
     uint16_t current_token_index;
     char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1];
-    bool need_token_update;
     TokenInfo* current_token;
-    uint32_t last_token_gen_time;
     TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context;
     NotificationMessage const** notification_sequence_new_token;
-    NotificationMessage const** notification_sequence_badusb;
+    NotificationMessage const** notification_sequence_automation;
     FuriMutex* last_code_update_sync;
+    TotpGenerateCodeWorkerContext* generate_code_worker_context;
+    uint8_t progress_bar_x;
+    uint8_t progress_bar_width;
 } SceneState;
 
 static const NotificationSequence*
@@ -80,7 +77,7 @@ static const NotificationSequence*
 
 static const NotificationSequence*
     get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) {
-    if(scene_state->notification_sequence_badusb == NULL) {
+    if(scene_state->notification_sequence_automation == NULL) {
         uint8_t i = 0;
         uint8_t length = 3;
         if(plugin_state->notification_method & NotificationMethodVibro) {
@@ -91,84 +88,46 @@ static const NotificationSequence*
             length += 6;
         }
 
-        scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length);
-        furi_check(scene_state->notification_sequence_badusb != NULL);
+        scene_state->notification_sequence_automation = malloc(sizeof(void*) * length);
+        furi_check(scene_state->notification_sequence_automation != NULL);
 
-        scene_state->notification_sequence_badusb[i++] = &message_blue_255;
+        scene_state->notification_sequence_automation[i++] = &message_blue_255;
         if(plugin_state->notification_method & NotificationMethodVibro) {
-            scene_state->notification_sequence_badusb[i++] = &message_vibro_on;
+            scene_state->notification_sequence_automation[i++] = &message_vibro_on;
         }
 
         if(plugin_state->notification_method & NotificationMethodSound) {
-            scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525
-            scene_state->notification_sequence_badusb[i++] = &message_delay_50;
-            scene_state->notification_sequence_badusb[i++] = &message_note_e4;
-            scene_state->notification_sequence_badusb[i++] = &message_delay_50;
-            scene_state->notification_sequence_badusb[i++] = &message_note_f3;
+            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;
         }
 
-        scene_state->notification_sequence_badusb[i++] = &message_delay_50;
+        scene_state->notification_sequence_automation[i++] = &message_delay_50;
 
         if(plugin_state->notification_method & NotificationMethodVibro) {
-            scene_state->notification_sequence_badusb[i++] = &message_vibro_off;
+            scene_state->notification_sequence_automation[i++] = &message_vibro_off;
         }
 
         if(plugin_state->notification_method & NotificationMethodSound) {
-            scene_state->notification_sequence_badusb[i++] = &message_sound_off;
+            scene_state->notification_sequence_automation[i++] = &message_sound_off;
         }
 
-        scene_state->notification_sequence_badusb[i++] = NULL;
+        scene_state->notification_sequence_automation[i++] = NULL;
     }
 
-    return (NotificationSequence*)scene_state->notification_sequence_badusb;
-}
-
-static void
-    int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) {
-    if(i_token_code == OTP_ERROR) {
-        memset(&str[0], '-', len);
-    } else {
-        if(algo == STEAM) {
-            for(uint8_t i = 0; i < len; i++) {
-                str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26];
-                i_token_code = i_token_code / 26;
-            }
-        } else {
-            for(int8_t i = len - 1; i >= 0; i--) {
-                str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
-                i_token_code = i_token_code / 10;
-            }
-        }
-    }
-
-    str[len] = '\0';
-}
-
-static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
-    switch(algo) {
-    case SHA1:
-    case STEAM:
-        return TOTP_ALGO_SHA1;
-    case SHA256:
-        return TOTP_ALGO_SHA256;
-    case SHA512:
-        return TOTP_ALGO_SHA512;
-    default:
-        break;
-    }
-
-    return NULL;
+    return (NotificationSequence*)scene_state->notification_sequence_automation;
 }
 
 static void update_totp_params(PluginState* const plugin_state) {
     SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
 
     if(scene_state->current_token_index < plugin_state->tokens_count) {
-        TokenInfo* tokenInfo =
+        scene_state->current_token =
             list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data;
-
-        scene_state->need_token_update = true;
-        scene_state->current_token = tokenInfo;
+        totp_generate_code_worker_notify(
+            scene_state->generate_code_worker_context, TotpGenerateCodeWorkerEventForceUpdate);
     }
 }
 
@@ -194,6 +153,24 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s
     }
 }
 
+static void on_new_token_code_generated(bool time_left, void* context) {
+    if(time_left) {
+        PluginState* plugin_state = context;
+        notification_message(
+            plugin_state->notification_app,
+            get_notification_sequence_new_token(plugin_state, plugin_state->current_scene_state));
+    }
+}
+
+static void on_code_lifetime_updated_generated(float code_lifetime_percent, void* context) {
+    SceneState* scene_state = context;
+    scene_state->progress_bar_width =
+        (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * code_lifetime_percent);
+    scene_state->progress_bar_x =
+        ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - scene_state->progress_bar_width) >> 1) +
+        PROGRESS_BAR_MARGIN;
+}
+
 void totp_scene_generate_token_activate(
     PluginState* plugin_state,
     const GenerateTokenSceneContext* context) {
@@ -231,15 +208,14 @@ void totp_scene_generate_token_activate(
     } else {
         scene_state->current_token_index = context->current_token_index;
     }
-    scene_state->need_token_update = true;
+
     plugin_state->current_scene_state = scene_state;
     FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
-    update_totp_params(plugin_state);
 
     scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal);
     if(plugin_state->automation_method & AutomationMethodBadUsb) {
         scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start(
-            &scene_state->last_code[0],
+            scene_state->last_code,
             TOTP_TOKEN_DIGITS_MAX_COUNT + 1,
             scene_state->last_code_update_sync);
     }
@@ -252,11 +228,28 @@ void totp_scene_generate_token_activate(
         }
         totp_bt_type_code_worker_start(
             plugin_state->bt_type_code_worker_context,
-            &scene_state->last_code[0],
+            scene_state->last_code,
             TOTP_TOKEN_DIGITS_MAX_COUNT + 1,
             scene_state->last_code_update_sync);
     }
 #endif
+
+    scene_state->generate_code_worker_context = totp_generate_code_worker_start(
+        scene_state->last_code,
+        &scene_state->current_token,
+        scene_state->last_code_update_sync,
+        plugin_state->timezone_offset,
+        plugin_state->iv);
+
+    totp_generate_code_worker_set_code_generated_handler(
+        scene_state->generate_code_worker_context, &on_new_token_code_generated, plugin_state);
+
+    totp_generate_code_worker_set_lifetime_changed_handler(
+        scene_state->generate_code_worker_context,
+        &on_code_lifetime_updated_generated,
+        scene_state);
+
+    update_totp_params(plugin_state);
 }
 
 void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) {
@@ -278,54 +271,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
         return;
     }
 
-    SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
-    FuriHalRtcDateTime curr_dt;
-    furi_hal_rtc_get_datetime(&curr_dt);
-    uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
-
-    bool is_new_token_time = curr_ts % scene_state->current_token->duration == 0;
-    if(is_new_token_time && scene_state->last_token_gen_time != curr_ts) {
-        scene_state->need_token_update = true;
-    }
-
-    if(scene_state->need_token_update) {
-        scene_state->need_token_update = false;
-        scene_state->last_token_gen_time = curr_ts;
-
-        const TokenInfo* tokenInfo = scene_state->current_token;
-
-        if(tokenInfo->token != NULL && tokenInfo->token_length > 0) {
-            furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
-            size_t key_length;
-            uint8_t* key = totp_crypto_decrypt(
-                tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length);
-
-            int_token_to_str(
-                totp_at(
-                    get_totp_algo_impl(tokenInfo->algo),
-                    key,
-                    key_length,
-                    curr_ts,
-                    plugin_state->timezone_offset,
-                    tokenInfo->duration),
-                scene_state->last_code,
-                tokenInfo->digits,
-                tokenInfo->algo);
-            memset_s(key, key_length, 0, key_length);
-            free(key);
-        } else {
-            furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
-            int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo);
-        }
-
-        furi_mutex_release(scene_state->last_code_update_sync);
-
-        if(is_new_token_time) {
-            notification_message(
-                plugin_state->notification_app,
-                get_notification_sequence_new_token(plugin_state, scene_state));
-        }
-    }
+    const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
 
     canvas_set_font(canvas, FontPrimary);
     uint16_t token_name_width = canvas_string_width(canvas, scene_state->current_token->name);
@@ -353,17 +299,11 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
 
     draw_totp_code(canvas, scene_state);
 
-    const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration;
-    float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME;
-    uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * percentDone);
-    uint8_t barX =
-        ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - barWidth) >> 1) + PROGRESS_BAR_MARGIN;
-
     canvas_draw_box(
         canvas,
-        barX,
+        scene_state->progress_bar_x,
         SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT,
-        barWidth,
+        scene_state->progress_bar_width,
         PROGRESS_BAR_HEIGHT);
 
     if(plugin_state->tokens_count > 1) {
@@ -493,6 +433,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
     if(plugin_state->current_scene_state == NULL) return;
     SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
 
+    totp_generate_code_worker_stop(scene_state->generate_code_worker_context);
+
     if(plugin_state->automation_method & AutomationMethodBadUsb) {
         totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context);
     }
@@ -506,8 +448,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
         free(scene_state->notification_sequence_new_token);
     }
 
-    if(scene_state->notification_sequence_badusb != NULL) {
-        free(scene_state->notification_sequence_badusb);
+    if(scene_state->notification_sequence_automation != NULL) {
+        free(scene_state->notification_sequence_automation);
     }
 
     furi_mutex_free(scene_state->last_code_update_sync);

+ 19 - 19
workers/bt_type_code/bt_type_code.c

@@ -1,10 +1,11 @@
 #include "bt_type_code.h"
 #include <furi_hal_bt_hid.h>
+#include <furi_hal_version.h>
 #include <bt/bt_service/bt_i.h>
 #include <storage/storage.h>
 #include "../../types/common.h"
 #include "../../types/token_info.h"
-#include "../common.h"
+#include "../type-code-common.h"
 
 #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys")
 
@@ -16,15 +17,15 @@ static inline bool totp_type_code_worker_stop_requested() {
 static void totp_type_code_worker_bt_set_app_mac(uint8_t* mac) {
     uint8_t max_i;
     size_t uid_size = furi_hal_version_uid_size();
-    if(uid_size < 6) {
+    if(uid_size < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN) {
         max_i = uid_size;
     } else {
-        max_i = 6;
+        max_i = TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN;
     }
 
     const uint8_t* uid = furi_hal_version_uid();
     memcpy(mac, uid, max_i);
-    for(uint8_t i = max_i; i < 6; i++) {
+    for(uint8_t i = max_i; i < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN; i++) {
         mac[i] = 0;
     }
 
@@ -39,23 +40,21 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context
         i++;
     } while(!context->is_connected && i < 100 && !totp_type_code_worker_stop_requested());
 
-    if(context->is_connected && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) {
+    if(context->is_connected &&
+       furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) {
         totp_type_code_worker_execute_automation(
             &furi_hal_bt_hid_kb_press,
             &furi_hal_bt_hid_kb_release,
-            context->string,
-            context->string_length,
+            context->code_buffer,
+            context->code_buffer_size,
             context->flags);
-        furi_mutex_release(context->string_sync);
+        furi_mutex_release(context->code_buffer_sync);
     }
 }
 
 static int32_t totp_type_code_worker_callback(void* context) {
     furi_check(context);
     FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if(context_mutex == NULL) {
-        return 251;
-    }
 
     TotpBtTypeCodeWorkerContext* bt_context = context;
 
@@ -92,13 +91,13 @@ static void connection_status_changed_callback(BtStatus status, void* context) {
 
 void totp_bt_type_code_worker_start(
     TotpBtTypeCodeWorkerContext* context,
-    char* code_buf,
-    uint8_t code_buf_length,
-    FuriMutex* code_buf_update_sync) {
+    char* code_buffer,
+    uint8_t code_buffer_size,
+    FuriMutex* code_buffer_sync) {
     furi_check(context != NULL);
-    context->string = code_buf;
-    context->string_length = code_buf_length;
-    context->string_sync = code_buf_update_sync;
+    context->code_buffer = code_buffer;
+    context->code_buffer_size = code_buffer_size;
+    context->code_buffer_sync = code_buffer_sync;
     context->thread = furi_thread_alloc();
     furi_thread_set_name(context->thread, "TOTPBtHidWorker");
     furi_thread_set_stack_size(context->thread, 1024);
@@ -137,7 +136,6 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
     bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH);
 
 #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
-    totp_type_code_worker_bt_set_app_mac(&context->bt_mac[0]);
     memcpy(
         &context->previous_bt_name[0],
         furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard),
@@ -148,8 +146,10 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
         TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN);
     char new_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN];
     snprintf(new_name, sizeof(new_name), "%s TOTP Auth", furi_hal_version_get_name_ptr());
+    uint8_t new_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
+    totp_type_code_worker_bt_set_app_mac(new_bt_mac);
     furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, new_name);
-    furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->bt_mac);
+    furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, new_bt_mac);
 #endif
 
     if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) {

+ 13 - 12
workers/bt_type_code/bt_type_code.h

@@ -1,8 +1,10 @@
 #pragma once
 
 #include <stdlib.h>
-#include <furi/furi.h>
-#include <furi_hal.h>
+#include <furi/core/thread.h>
+#include <furi/core/mutex.h>
+#include <furi/core/string.h>
+#include <furi/core/kernel.h>
 #include <bt/bt_service/bt.h>
 #include "../../features_config.h"
 
@@ -14,34 +16,33 @@
 typedef uint8_t TotpBtTypeCodeWorkerEvent;
 
 typedef struct {
-    char* string;
-    uint8_t string_length;
+    char* code_buffer;
+    uint8_t code_buffer_size;
     uint8_t flags;
     FuriThread* thread;
-    FuriMutex* string_sync;
+    FuriMutex* code_buffer_sync;
     Bt* bt;
     bool is_advertising;
     bool is_connected;
 #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
-    uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
     char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN];
     uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
 #endif
 } TotpBtTypeCodeWorkerContext;
 
 enum TotpBtTypeCodeWorkerEvents {
-    TotpBtTypeCodeWorkerEventReserved = 0b0000,
-    TotpBtTypeCodeWorkerEventStop = 0b0100,
-    TotpBtTypeCodeWorkerEventType = 0b1000
+    TotpBtTypeCodeWorkerEventReserved = 0b00,
+    TotpBtTypeCodeWorkerEventStop = 0b01,
+    TotpBtTypeCodeWorkerEventType = 0b10
 };
 
 TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init();
 void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context);
 void totp_bt_type_code_worker_start(
     TotpBtTypeCodeWorkerContext* context,
-    char* code_buf,
-    uint8_t code_buf_length,
-    FuriMutex* code_buf_update_sync);
+    char* code_buffer,
+    uint8_t code_buffer_size,
+    FuriMutex* code_buffer_sync);
 void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context);
 void totp_bt_type_code_worker_notify(
     TotpBtTypeCodeWorkerContext* context,

+ 181 - 0
workers/generate_totp_code/generate_totp_code.c

@@ -0,0 +1,181 @@
+#include "generate_totp_code.h"
+#include "../../services/crypto/crypto.h"
+#include "../../services/totp/totp.h"
+#include "../../services/convert/convert.h"
+#include <furi_hal_rtc.h>
+#include <memset_s.h>
+
+#define ONE_SEC_MS (1000)
+
+static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
+
+static void
+    int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) {
+    str[len] = '\0';
+    if(i_token_code == OTP_ERROR) {
+        memset(&str[0], '-', len);
+    } else {
+        if(algo == STEAM) {
+            for(uint8_t i = 0; i < len; i++) {
+                str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26];
+                i_token_code = i_token_code / 26;
+            }
+        } else {
+            for(int8_t i = len - 1; i >= 0; i--) {
+                str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
+                i_token_code = i_token_code / 10;
+            }
+        }
+    }
+}
+
+static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
+    switch(algo) {
+    case SHA1:
+    case STEAM:
+        return TOTP_ALGO_SHA1;
+    case SHA256:
+        return TOTP_ALGO_SHA256;
+    case SHA512:
+        return TOTP_ALGO_SHA512;
+    default:
+        break;
+    }
+
+    return NULL;
+}
+
+static void generate_totp_code(
+    TotpGenerateCodeWorkerContext* context,
+    const TokenInfo* token_info,
+    uint32_t current_ts) {
+    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, &key_length);
+
+        int_token_to_str(
+            totp_at(
+                get_totp_algo_impl(token_info->algo),
+                key,
+                key_length,
+                current_ts,
+                context->timezone_offset,
+                token_info->duration),
+            context->code_buffer,
+            token_info->digits,
+            token_info->algo);
+        memset_s(key, key_length, 0, key_length);
+        free(key);
+    } else {
+        int_token_to_str(0, context->code_buffer, token_info->digits, token_info->algo);
+    }
+}
+
+static int32_t totp_generate_worker_callback(void* context) {
+    furi_check(context);
+
+    TotpGenerateCodeWorkerContext* t_context = context;
+
+    while(true) {
+        uint32_t flags = furi_thread_flags_wait(
+            TotpGenerateCodeWorkerEventStop | TotpGenerateCodeWorkerEventForceUpdate,
+            FuriFlagWaitAny,
+            ONE_SEC_MS);
+
+        if(flags ==
+           (uint32_t)
+               FuriFlagErrorTimeout) { // If timeout, consider as no error, as we expect this and can handle gracefully
+            flags = 0;
+        }
+
+        furi_check((flags & FuriFlagError) == 0); //-V562
+
+        if(flags & TotpGenerateCodeWorkerEventStop) break;
+
+        const TokenInfo* token_info = *(t_context->token_info);
+        if(token_info == NULL) {
+            continue;
+        }
+
+        uint32_t curr_ts = furi_hal_rtc_get_timestamp();
+
+        bool time_left = false;
+        if(flags & TotpGenerateCodeWorkerEventForceUpdate ||
+           (time_left = (curr_ts % token_info->duration) == 0)) {
+            if(furi_mutex_acquire(t_context->code_buffer_sync, FuriWaitForever) == FuriStatusOk) {
+                generate_totp_code(t_context, token_info, curr_ts);
+                curr_ts = furi_hal_rtc_get_timestamp();
+                furi_mutex_release(t_context->code_buffer_sync);
+                if(t_context->on_new_code_generated_handler != NULL) {
+                    (*(t_context->on_new_code_generated_handler))(
+                        time_left, t_context->on_new_code_generated_handler_context);
+                }
+            }
+        }
+
+        if(t_context->on_code_lifetime_changed_handler != NULL) {
+            (*(t_context->on_code_lifetime_changed_handler))(
+                (float)(token_info->duration - curr_ts % token_info->duration) /
+                    (float)token_info->duration,
+                t_context->on_code_lifetime_changed_handler_context);
+        }
+    }
+
+    return 0;
+}
+
+TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
+    char* code_buffer,
+    TokenInfo** token_info,
+    FuriMutex* code_buffer_sync,
+    float timezone_offset,
+    uint8_t* iv) {
+    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->thread = furi_thread_alloc();
+    furi_thread_set_name(context->thread, "TOTPGenerateWorker");
+    furi_thread_set_stack_size(context->thread, 2048);
+    furi_thread_set_context(context->thread, context);
+    furi_thread_set_callback(context->thread, totp_generate_worker_callback);
+    furi_thread_start(context->thread);
+    return context;
+}
+
+void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context) {
+    furi_check(context != NULL);
+    furi_thread_flags_set(furi_thread_get_id(context->thread), TotpGenerateCodeWorkerEventStop);
+    furi_thread_join(context->thread);
+    furi_thread_free(context->thread);
+    free(context);
+}
+
+void totp_generate_code_worker_notify(
+    TotpGenerateCodeWorkerContext* context,
+    TotpGenerateCodeWorkerEvent event) {
+    furi_check(context != NULL);
+    furi_thread_flags_set(furi_thread_get_id(context->thread), event);
+}
+
+void totp_generate_code_worker_set_code_generated_handler(
+    TotpGenerateCodeWorkerContext* context,
+    TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler,
+    void* on_new_code_generated_handler_context) {
+    furi_check(context != NULL);
+    context->on_new_code_generated_handler = on_new_code_generated_handler;
+    context->on_new_code_generated_handler_context = on_new_code_generated_handler_context;
+}
+
+void totp_generate_code_worker_set_lifetime_changed_handler(
+    TotpGenerateCodeWorkerContext* context,
+    TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler,
+    void* on_code_lifetime_changed_handler_context) {
+    furi_check(context != NULL);
+    context->on_code_lifetime_changed_handler = on_code_lifetime_changed_handler;
+    context->on_code_lifetime_changed_handler_context = on_code_lifetime_changed_handler_context;
+}

+ 49 - 0
workers/generate_totp_code/generate_totp_code.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include <stdlib.h>
+#include <furi/core/thread.h>
+#include <furi/core/mutex.h>
+#include "../../types/token_info.h"
+
+typedef uint8_t TotpGenerateCodeWorkerEvent;
+
+typedef void (*TOTP_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context);
+typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context);
+
+typedef struct {
+    char* code_buffer;
+    FuriThread* thread;
+    FuriMutex* code_buffer_sync;
+    TokenInfo** token_info;
+    float timezone_offset;
+    uint8_t* iv;
+    TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler;
+    void* on_new_code_generated_handler_context;
+    TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler;
+    void* on_code_lifetime_changed_handler_context;
+} TotpGenerateCodeWorkerContext;
+
+enum TotGenerateCodeWorkerEvents {
+    TotpGenerateCodeWorkerEventReserved = 0b00,
+    TotpGenerateCodeWorkerEventStop = 0b01,
+    TotpGenerateCodeWorkerEventForceUpdate = 0b10
+};
+
+TotpGenerateCodeWorkerContext* totp_generate_code_worker_start(
+    char* code_buffer,
+    TokenInfo** token_info,
+    FuriMutex* code_buffer_sync,
+    float timezone_offset,
+    uint8_t* iv);
+void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context);
+void totp_generate_code_worker_notify(
+    TotpGenerateCodeWorkerContext* context,
+    TotpGenerateCodeWorkerEvent event);
+void totp_generate_code_worker_set_code_generated_handler(
+    TotpGenerateCodeWorkerContext* context,
+    TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler,
+    void* on_new_code_generated_handler_context);
+void totp_generate_code_worker_set_lifetime_changed_handler(
+    TotpGenerateCodeWorkerContext* context,
+    TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler,
+    void* on_code_lifetime_changed_handler_context);

+ 8 - 8
workers/common.c → workers/type-code-common.c

@@ -1,6 +1,6 @@
-#include "common.h"
-#include <furi/furi.h>
-#include <furi_hal.h>
+#include "type-code-common.h"
+#include <furi_hal_usb_hid.h>
+#include <furi/core/kernel.h>
 #include "../../services/convert/convert.h"
 
 static const uint8_t hid_number_keys[] = {
@@ -42,18 +42,18 @@ static void totp_type_code_worker_press_key(
 void totp_type_code_worker_execute_automation(
     TOTP_AUTOMATION_KEY_HANDLER key_press_fn,
     TOTP_AUTOMATION_KEY_HANDLER key_release_fn,
-    const char* string,
-    uint8_t string_length,
+    const char* code_buffer,
+    uint8_t code_buffer_size,
     TokenAutomationFeature features) {
     furi_delay_ms(500);
     uint8_t i = 0;
     totp_type_code_worker_press_key(
         HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features);
 
-    while(i < string_length && string[i] != 0) {
-        uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]);
+    while(i < code_buffer_size && code_buffer[i] != 0) {
+        uint8_t char_index = CONVERT_CHAR_TO_DIGIT(code_buffer[i]);
         if(char_index > 9) {
-            char_index = string[i] - 0x41 + 10;
+            char_index = code_buffer[i] - 0x41 + 10;
         }
 
         if(char_index > 35) break;

+ 2 - 2
workers/common.h → workers/type-code-common.h

@@ -7,6 +7,6 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key);
 void totp_type_code_worker_execute_automation(
     TOTP_AUTOMATION_KEY_HANDLER key_press_fn,
     TOTP_AUTOMATION_KEY_HANDLER key_release_fn,
-    const char* string,
-    uint8_t string_length,
+    const char* code_buffer,
+    uint8_t code_buffer_size,
     TokenAutomationFeature features);

+ 12 - 14
workers/usb_type_code/usb_type_code.c

@@ -1,7 +1,8 @@
 #include "usb_type_code.h"
+#include <furi_hal_usb_hid.h>
 #include "../../services/convert/convert.h"
 #include "../../types/token_info.h"
-#include "../common.h"
+#include "../type-code-common.h"
 
 static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) {
     if(context->usb_mode_prev != NULL) {
@@ -25,14 +26,14 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex
     } while(!furi_hal_hid_is_connected() && i < 100 && !totp_type_code_worker_stop_requested());
 
     if(furi_hal_hid_is_connected() &&
-       furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) {
+       furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) {
         totp_type_code_worker_execute_automation(
             &furi_hal_hid_kb_press,
             &furi_hal_hid_kb_release,
-            context->string,
-            context->string_length,
+            context->code_buffer,
+            context->code_buffer_size,
             context->flags);
-        furi_mutex_release(context->string_sync);
+        furi_mutex_release(context->code_buffer_sync);
 
         furi_delay_ms(100);
     }
@@ -43,9 +44,6 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex
 static int32_t totp_type_code_worker_callback(void* context) {
     furi_check(context);
     FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if(context_mutex == NULL) {
-        return 251;
-    }
 
     while(true) {
         uint32_t flags = furi_thread_flags_wait(
@@ -70,14 +68,14 @@ static int32_t totp_type_code_worker_callback(void* context) {
 }
 
 TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
-    char* code_buf,
-    uint8_t code_buf_length,
-    FuriMutex* code_buf_update_sync) {
+    char* code_buffer,
+    uint8_t code_buffer_size,
+    FuriMutex* code_buffer_sync) {
     TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext));
     furi_check(context != NULL);
-    context->string = code_buf;
-    context->string_length = code_buf_length;
-    context->string_sync = code_buf_update_sync;
+    context->code_buffer = code_buffer;
+    context->code_buffer_size = code_buffer_size;
+    context->code_buffer_sync = code_buffer_sync;
     context->thread = furi_thread_alloc();
     context->usb_mode_prev = NULL;
     furi_thread_set_name(context->thread, "TOTPUsbHidWorker");

+ 11 - 8
workers/usb_type_code/usb_type_code.h

@@ -1,17 +1,20 @@
 #pragma once
 
 #include <stdlib.h>
-#include <furi/furi.h>
-#include <furi_hal.h>
+#include <furi/core/thread.h>
+#include <furi/core/mutex.h>
+#include <furi/core/kernel.h>
+#include <furi/core/check.h>
+#include <furi_hal_usb.h>
 
 typedef uint8_t TotpUsbTypeCodeWorkerEvent;
 
 typedef struct {
-    char* string;
-    uint8_t string_length;
+    char* code_buffer;
+    uint8_t code_buffer_size;
     uint8_t flags;
     FuriThread* thread;
-    FuriMutex* string_sync;
+    FuriMutex* code_buffer_sync;
     FuriHalUsbInterface* usb_mode_prev;
 } TotpUsbTypeCodeWorkerContext;
 
@@ -22,9 +25,9 @@ enum TotpUsbTypeCodeWorkerEvents {
 };
 
 TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
-    char* code_buf,
-    uint8_t code_buf_length,
-    FuriMutex* code_buf_update_sync);
+    char* code_buffer,
+    uint8_t code_buffer_size,
+    FuriMutex* code_buffer_sync);
 void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context);
 void totp_usb_type_code_worker_notify(
     TotpUsbTypeCodeWorkerContext* context,