MX 1 سال پیش
والد
کامیت
8c39cdeb29

+ 1 - 1
application.fam

@@ -7,7 +7,7 @@ App(
     requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"],
     stack_size=2 * 1024,
     order=20,
-    fap_version="5.110",
+    fap_version="5.120",
     fap_author="Alexander Kopachov (@akopachov)",
     fap_description="Software-based TOTP/HOTP authenticator for Flipper Zero device",
     fap_weburl="https://github.com/akopachov/flipper-zero_authenticator",

+ 59 - 59
assets/cli/cli_help.txt

@@ -1,59 +1,59 @@
-Usage:
-  totp (help | h | ?)
-  totp version
-  totp (list | ls)
-  totp (lsattr | cat) <index>
-  totp (add | mk | new) <name> [-t <type>] [-i <counter>] [-a <algo>] [-e <encoding>] [-d <digits>] [-l <duration>] [-u] [-b <feature>]...
-  totp (update) <index> [-t <type>] [-i <counter>] [-a <algo>] [-e <encoding>] [-n <name>] [-d <digits>] [-l <duration>] [-u] [-s] [-b <feature>]...
-  totp (delete | rm) <index> [-f]
-  totp (move | mv) <index> <new_index>
-  totp pin (set | remove) [-c <slot>]
-  totp notify [<notification>...]
-  totp (timezone | tz) [<timezone>]
-  totp reset
-  totp automation [-k <layout>] [-w <delay>] [<automation>...]
-  totp export
-
-Commands:
-  help, h, ?       Show command usage help
-  version          Get application version
-  list, ls         List all available tokens
-  lsattr, cat      Displays token details
-  add, mk, new     Add new token
-  update           Update existing token
-  delete, rm       Delete existing token
-  move, mv         Move token
-  pin              Set\change\remove PIN
-  notify           Get or set notification method
-  timezone, tz     Get or set current timezone
-  reset            Reset application to default settings
-  automation       Get or set automation settings
-  export           Exports and prints all the tokens into URI-list format
-
-Arguments:
-  name          Token name
-  index         Token index in the list
-  new_index     New token index in the list
-  notification  Notification method to be set. Must be one of: none, sound, vibro
-  timezone      Timezone offset in hours to be set
-  automation    Automation method to be set. Must be one of: none, usb, bt
-
-Options:
-  -t <type>      Token type. Must be one of: totp, hotp [default: totp]
-  -i <counter>   Token initial counter. Applicable for HOTP tokens only. Must be positive integer number [default: 0]
-  -a <algo>      Token hashing algorithm. Must be one of: sha1, sha256, sha512, steam [default: sha1]
-  -d <digits>    Token digits count. Must be one of: 5, 6, 8 [default: 6]
-  -e <encoding>  Token secret encoding, one of base32, base64 [default: base32]
-  -l <duration>  Token lifetime duration in seconds. Applicable for TOTP tokens only. Must be between: 15 and 255 [default: 30]
-  -u             Show console user input as-is without masking
-  -b <feature>   Token automation features to be enabled. Must be one of: none, enter, tab [default: none]
-                 # none - No features
-                 # enter - Type <Enter> key at the end of token input automation
-                 # tab - Type <Tab> key at the end of token input automation
-                 # slower - Type slower
-  -n <name>      Token name
-  -s             Update token secret
-  -f             Force command to do not ask user for interactive confirmation
-  -c <slot>      New crypto key slot. Must be between 12 and 100
-  -k <layout>    Automation keyboard layout. Must be one of: QWERTY, AZERTY, QWERTZ, Czech, Dvorak, Hungarian, Slovak
-  -w <delay>     Automation initial delay in seconds. Must be positive float value [default: 0.5]
+Usage:
+  totp (help | h | ?)
+  totp version
+  totp (list | ls)
+  totp (lsattr | cat) <index>
+  totp (add | mk | new) <name> [-t <type>] [-i <counter>] [-a <algo>] [-e <encoding>] [-d <digits>] [-l <duration>] [-u] [-b <feature>]...
+  totp (update) <index> [-t <type>] [-i <counter>] [-a <algo>] [-e <encoding>] [-n <name>] [-d <digits>] [-l <duration>] [-u] [-s] [-b <feature>]...
+  totp (delete | rm) <index> [-f]
+  totp (move | mv) <index> <new_index>
+  totp pin (set | remove) [-c <slot>]
+  totp notify [<notification>...]
+  totp (timezone | tz) [<timezone>]
+  totp reset
+  totp automation [-k <layout>] [-w <delay>] [<automation>...]
+  totp export
+
+Commands:
+  help, h, ?       Show command usage help
+  version          Get application version
+  list, ls         List all available tokens
+  lsattr, cat      Displays token details
+  add, mk, new     Add new token
+  update           Update existing token
+  delete, rm       Delete existing token
+  move, mv         Move token
+  pin              Set\change\remove PIN
+  notify           Get or set notification method
+  timezone, tz     Get or set current timezone
+  reset            Reset application to default settings
+  automation       Get or set automation settings
+  export           Exports and prints all the tokens into URI-list format
+
+Arguments:
+  name          Token name
+  index         Token index in the list
+  new_index     New token index in the list
+  notification  Notification method to be set. Must be one of: none, sound, vibro
+  timezone      Timezone offset in hours to be set
+  automation    Automation method to be set. Must be one of: none, usb, bt
+
+Options:
+  -t <type>      Token type. Must be one of: totp, hotp [default: totp]
+  -i <counter>   Token initial counter. Applicable for HOTP tokens only. Must be positive integer number [default: 0]
+  -a <algo>      Token hashing algorithm. Must be one of: sha1, sha256, sha512, steam [default: sha1]
+  -d <digits>    Token digits count. Must be one of: 5, 6, 8 [default: 6]
+  -e <encoding>  Token secret encoding, one of base32, base64 [default: base32]
+  -l <duration>  Token lifetime duration in seconds. Applicable for TOTP tokens only. Must be between: 15 and 255 [default: 30]
+  -u             Show console user input as-is without masking
+  -b <feature>   Token automation features to be enabled. Must be one of: none, enter, tab [default: none]
+                 # none - No features
+                 # enter - Type <Enter> key at the end of token input automation
+                 # tab - Type <Tab> key at the end of token input automation
+                 # slower - Type slower
+  -n <name>      Token name
+  -s             Update token secret
+  -f             Force command to do not ask user for interactive confirmation
+  -c <slot>      New crypto key slot. Must be between 12 and 100
+  -k <layout>    Automation keyboard layout. Must be one of: QWERTY, AZERTY, QWERTZ, Czech, Dvorak, Hungarian, Slovak
+  -w <delay>     Automation initial delay in seconds. Must be positive float value [default: 0.5]

+ 8 - 4
services/config/config.c

@@ -738,15 +738,19 @@ bool totp_config_file_ensure_latest_encryption(
     uint8_t pin_length) {
     bool result = true;
     if(plugin_state->crypto_settings.crypto_version < CRYPTO_LATEST_VERSION) {
-        FURI_LOG_I(LOGGING_TAG, "Migration crypto from v%" PRIu8 " to v%" PRIu8 " is needed", plugin_state->crypto_settings.crypto_version, CRYPTO_LATEST_VERSION);
-        
+        FURI_LOG_I(
+            LOGGING_TAG,
+            "Migration crypto from v%" PRIu8 " to v%" PRIu8 " is needed",
+            plugin_state->crypto_settings.crypto_version,
+            CRYPTO_LATEST_VERSION);
+
 #ifndef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED
-        if (plugin_state->crypto_settings.crypto_version == 1) {
+        if(plugin_state->crypto_settings.crypto_version == 1) {
             furi_crash("Authenticator: Crypto v1 is not supported");
         }
 #endif
 #ifndef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED
-        if (plugin_state->crypto_settings.crypto_version == 2) {
+        if(plugin_state->crypto_settings.crypto_version == 2) {
             furi_crash("Authenticator: Crypto v2 is not supported");
         }
 #endif

+ 1 - 1
services/idle_timeout/idle_timeout.c

@@ -3,7 +3,7 @@
 #include <furi/core/timer.h>
 
 #define IDLE_TIMER_CHECK_PERIODICITY_SEC (1)
-#define SEC_TO_TICKS(sec) ((sec)*1000)
+#define SEC_TO_TICKS(sec) ((sec) * 1000)
 
 struct IdleTimeoutContext {
     FuriTimer* timer;

+ 2 - 1
totp_app.c

@@ -166,7 +166,8 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
 
 #ifdef TOTP_BADBT_AUTOMATION_ENABLED
     if(plugin_state->automation_method & AutomationMethodBadBt) {
-        plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init();
+        plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init(
+            *((uint16_t*)plugin_state->crypto_settings.crypto_verify_data));
     } else {
         plugin_state->bt_type_code_worker_context = NULL;
     }

+ 2 - 2
types/token_info.c

@@ -8,8 +8,8 @@
 #include "common.h"
 #include "../services/crypto/crypto_facade.h"
 
-#define ESTIMATE_BASE32_PLAIN_LENGTH(base32_length) ((base32_length)*0.625f)
-#define ESTIMATE_BASE64_PLAIN_LENGTH(base64_length) ((base64_length)*0.75f)
+#define ESTIMATE_BASE32_PLAIN_LENGTH(base32_length) ((base32_length) * 0.625f)
+#define ESTIMATE_BASE64_PLAIN_LENGTH(base64_length) ((base64_length) * 0.75f)
 
 TokenInfo* token_info_alloc() {
     TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));

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

@@ -201,7 +201,8 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) {
 
     if(plugin_state->automation_method & AutomationMethodBadBt) {
         if(plugin_state->bt_type_code_worker_context == NULL) {
-            plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init();
+            plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init(
+                *((uint16_t*)plugin_state->crypto_settings.crypto_verify_data));
         }
         totp_bt_type_code_worker_start(
             plugin_state->bt_type_code_worker_context,

+ 1 - 1
version.h

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

+ 21 - 28
workers/bt_type_code/bt_type_code.c

@@ -23,9 +23,9 @@ struct TotpBtTypeCodeWorkerContext {
     FuriThread* thread;
     FuriMutex* code_buffer_sync;
     Bt* bt;
+    FuriHalBleProfileBase* ble_hid_profile;
     bool is_advertising;
     bool is_connected;
-    FuriHalBleProfileBase* ble_hid_profile;
     AutomationKeyboardLayout keyboard_layout;
     uint16_t initial_delay;
 };
@@ -34,23 +34,15 @@ static inline bool totp_type_code_worker_stop_requested() {
     return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
 }
 
-// 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 < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN) {
-//         max_i = uid_size;
-//     } else {
-//         max_i = TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN;
-//     }
-
-//     const uint8_t* uid = (const uint8_t*)UID64_BASE; //-V566
-//     memcpy(mac, uid, max_i);
-//     for(uint8_t i = max_i; i < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN; i++) {
-//         mac[i] = 0;
-//     }
+static bool hid_key_press(uint16_t button, void* context) {
+    FuriHalBleProfileBase* profile = context;
+    return ble_profile_hid_kb_press(profile, button);
+}
 
-//     mac[0] = 0b10;
-// }
+static bool hid_key_release(uint16_t button, void* context) {
+    FuriHalBleProfileBase* profile = context;
+    return ble_profile_hid_kb_release(profile, button);
+}
 
 static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) {
     uint8_t i = 0;
@@ -61,15 +53,15 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context
 
     if(context->is_connected &&
        furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) {
-        totp_type_code_worker_execute_automation_ctx(
-            (TOTP_AUTOMATION_KEY_HANDLER_CTX)&ble_profile_hid_kb_press,
-            (TOTP_AUTOMATION_KEY_HANDLER_CTX)&ble_profile_hid_kb_release,
-            context->ble_hid_profile,
+        totp_type_code_worker_execute_automation(
+            &hid_key_press,
+            &hid_key_release,
             context->code_buffer,
             context->code_buffer_size,
             context->flags,
             context->keyboard_layout,
-            context->initial_delay);
+            context->initial_delay,
+            context->ble_hid_profile);
         furi_mutex_release(context->code_buffer_sync);
     }
 }
@@ -149,7 +141,7 @@ void totp_bt_type_code_worker_notify(
     furi_thread_flags_set(furi_thread_get_id(context->thread), event);
 }
 
-TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
+TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(uint16_t mac_xor) {
     TotpBtTypeCodeWorkerContext* context = malloc(sizeof(TotpBtTypeCodeWorkerContext));
     furi_check(context != NULL);
 
@@ -160,10 +152,8 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
     furi_delay_ms(200);
     bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH);
 
-    BleProfileHidParams params = {
-        .device_name_prefix = "TOTP",
-    };
-    context->ble_hid_profile = bt_profile_start(context->bt, ble_profile_hid, &params);
+    BleProfileHidParams ble_params = {.device_name_prefix = "TOTP", .mac_xor = mac_xor};
+    context->ble_hid_profile = bt_profile_start(context->bt, ble_profile_hid, &ble_params);
     furi_check(context->ble_hid_profile);
 
     furi_hal_bt_start_advertising();
@@ -183,14 +173,17 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
 
     bt_set_status_changed_callback(context->bt, NULL, NULL);
 
+    furi_hal_bt_stop_advertising();
     context->is_advertising = false;
     context->is_connected = false;
 
     bt_disconnect(context->bt);
     furi_delay_ms(200);
     bt_keys_storage_set_default_path(context->bt);
+    if(!bt_profile_restore_default(context->bt)) {
+        FURI_LOG_E(LOGGING_TAG, "Failed to restore to default BT profile");
+    }
 
-    furi_check(bt_profile_restore_default(context->bt));
     furi_record_close(RECORD_BT);
     context->bt = NULL;
 

+ 2 - 1
workers/bt_type_code/bt_type_code.h

@@ -36,9 +36,10 @@ enum TotpBtTypeCodeWorkerEvents {
 
 /**
  * @brief Initializes bluetooth token input automation worker
+ * @param mac_xor value to be used to XOR BT MAC address to make it unique
  * @return worker context
  */
-TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init();
+TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(uint16_t mac_xor);
 
 /**
  * @brief Disposes bluetooth token input automation worker and releases all the allocated resources

+ 11 - 66
workers/type_code_common.c

@@ -25,10 +25,11 @@ static void totp_type_code_worker_press_key(
     uint16_t key,
     TOTP_AUTOMATION_KEY_HANDLER key_press_fn,
     TOTP_AUTOMATION_KEY_HANDLER key_release_fn,
-    TokenAutomationFeature features) {
-    (*key_press_fn)(key);
+    TokenAutomationFeature features,
+    void* context) {
+    (*key_press_fn)(key, context);
     furi_delay_ms(get_keypress_delay(features));
-    (*key_release_fn)(key);
+    (*key_release_fn)(key, context);
 }
 
 void totp_type_code_worker_execute_automation(
@@ -38,7 +39,8 @@ void totp_type_code_worker_execute_automation(
     uint8_t code_buffer_size,
     TokenAutomationFeature features,
     AutomationKeyboardLayout keyboard_layout,
-    uint16_t initial_delay) {
+    uint16_t initial_delay,
+    void* context) {
     uint16_t keyboard_layout_dict[TOTP_KB_LAYOUT_DATA_LENGTH];
     if(!totp_kb_layout_provider_get_layout_data(keyboard_layout, &keyboard_layout_dict[0])) {
         return;
@@ -59,78 +61,21 @@ void totp_type_code_worker_execute_automation(
         if(char_index >= TOTP_KB_LAYOUT_DATA_LENGTH) break;
 
         uint16_t hid_kb_key = keyboard_layout_dict[char_index];
-        totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features);
-        furi_delay_ms(keystroke_delay);
-        i++;
-    }
-
-    if(features & TokenAutomationFeatureEnterAtTheEnd) {
-        furi_delay_ms(keystroke_delay);
         totp_type_code_worker_press_key(
-            HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features);
-    }
-
-    if(features & TokenAutomationFeatureTabAtTheEnd) {
-        furi_delay_ms(keystroke_delay);
-        totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features);
-    }
-}
-
-static void totp_type_code_worker_press_key_ctx(
-    uint16_t key,
-    TOTP_AUTOMATION_KEY_HANDLER_CTX key_press_fn,
-    TOTP_AUTOMATION_KEY_HANDLER_CTX key_release_fn,
-    void* ctx,
-    TokenAutomationFeature features) {
-    (*key_press_fn)(ctx, key);
-    furi_delay_ms(get_keypress_delay(features));
-    (*key_release_fn)(ctx, key);
-}
-
-void totp_type_code_worker_execute_automation_ctx(
-    TOTP_AUTOMATION_KEY_HANDLER_CTX key_press_fn,
-    TOTP_AUTOMATION_KEY_HANDLER_CTX key_release_fn,
-    void* ctx,
-    const char* code_buffer,
-    uint8_t code_buffer_size,
-    TokenAutomationFeature features,
-    AutomationKeyboardLayout keyboard_layout,
-    uint16_t initial_delay) {
-    uint16_t keyboard_layout_dict[TOTP_KB_LAYOUT_DATA_LENGTH];
-    if(!totp_kb_layout_provider_get_layout_data(keyboard_layout, &keyboard_layout_dict[0])) {
-        return;
-    }
-
-    furi_delay_ms(initial_delay);
-
-    uint32_t keystroke_delay = get_keystroke_delay(features);
-
-    char cb_char;
-    uint8_t i = 0;
-    while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) {
-        uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char);
-        if(char_index > 9) {
-            char_index = cb_char - 'A' + 10;
-        }
-
-        if(char_index >= TOTP_KB_LAYOUT_DATA_LENGTH) break;
-
-        uint16_t hid_kb_key = keyboard_layout_dict[char_index];
-        totp_type_code_worker_press_key_ctx(
-            hid_kb_key, key_press_fn, key_release_fn, ctx, features);
+            hid_kb_key, key_press_fn, key_release_fn, features, context);
         furi_delay_ms(keystroke_delay);
         i++;
     }
 
     if(features & TokenAutomationFeatureEnterAtTheEnd) {
         furi_delay_ms(keystroke_delay);
-        totp_type_code_worker_press_key_ctx(
-            HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, ctx, features);
+        totp_type_code_worker_press_key(
+            HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features, context);
     }
 
     if(features & TokenAutomationFeatureTabAtTheEnd) {
         furi_delay_ms(keystroke_delay);
-        totp_type_code_worker_press_key_ctx(
-            HID_KEYBOARD_TAB, key_press_fn, key_release_fn, ctx, features);
+        totp_type_code_worker_press_key(
+            HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features, context);
     }
 }

+ 4 - 24
workers/type_code_common.h

@@ -3,8 +3,7 @@
 #include "../types/token_info.h"
 #include "../types/automation_kb_layout.h"
 
-typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key);
-typedef bool (*TOTP_AUTOMATION_KEY_HANDLER_CTX)(void* ctx, uint16_t key);
+typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key, void* context);
 
 /**
  * @brief Executes token input automation using given key press\release handlers
@@ -15,6 +14,7 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER_CTX)(void* ctx, uint16_t key);
  * @param features automation features
  * @param keyboard_layout keyboard layout to be used
  * @param initial_delay initial delay before starting automation
+ * @param context context to be passed to key press\release handlers
  */
 void totp_type_code_worker_execute_automation(
     TOTP_AUTOMATION_KEY_HANDLER key_press_fn,
@@ -23,25 +23,5 @@ void totp_type_code_worker_execute_automation(
     uint8_t code_buffer_size,
     TokenAutomationFeature features,
     AutomationKeyboardLayout keyboard_layout,
-    uint16_t initial_delay);
-
-/**
- * @brief Executes token input automation using given key press\release handlers
- * @param key_press_fn key press handler
- * @param key_release_fn key release handler
- * @param ctx user data context
- * @param code_buffer code buffer to be typed
- * @param code_buffer_size code buffer size
- * @param features automation features
- * @param keyboard_layout keyboard layout to be used
- * @param initial_delay initial delay before starting automation
- */
-void totp_type_code_worker_execute_automation_ctx(
-    TOTP_AUTOMATION_KEY_HANDLER_CTX key_press_fn,
-    TOTP_AUTOMATION_KEY_HANDLER_CTX key_release_fn,
-    void* ctx,
-    const char* code_buffer,
-    uint8_t code_buffer_size,
-    TokenAutomationFeature features,
-    AutomationKeyboardLayout keyboard_layout,
-    uint16_t initial_delay);
+    uint16_t initial_delay,
+    void* context);

+ 14 - 3
workers/usb_type_code/usb_type_code.c

@@ -30,6 +30,16 @@ static inline bool totp_type_code_worker_stop_requested() {
     return furi_thread_flags_get() & TotpUsbTypeCodeWorkerEventStop;
 }
 
+static bool hid_key_press(uint16_t button, void* context) {
+    UNUSED(context);
+    return furi_hal_hid_kb_press(button);
+}
+
+static bool hid_key_release(uint16_t button, void* context) {
+    UNUSED(context);
+    return furi_hal_hid_kb_release(button);
+}
+
 static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* context) {
     context->usb_mode_prev = furi_hal_usb_get_config();
     furi_hal_usb_unlock();
@@ -43,13 +53,14 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex
     if(furi_hal_hid_is_connected() &&
        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,
+            &hid_key_press,
+            &hid_key_release,
             context->code_buffer,
             context->code_buffer_size,
             context->flags,
             context->keyboard_layout,
-            context->initial_delay);
+            context->initial_delay,
+            NULL);
         furi_mutex_release(context->code_buffer_sync);
 
         furi_delay_ms(100);