This commit is contained in:
RogueMaster
2022-11-17 23:44:44 -05:00
parent 699819dd0f
commit c3273fcf49
31 changed files with 989 additions and 131 deletions
+191
View File
@@ -0,0 +1,191 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 99
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 10
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: Never
SortJavaStaticImport: Before
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: Never
SpaceBeforeParensOptions:
AfterControlStatements: false
AfterForeachMacros: false
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: false
AfterOverloadedOperator: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: c++03
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...
+1 -1
View File
@@ -1,5 +1,5 @@
App(
appid="Authenticator",
appid="totp",
name="Authenticator",
apptype=FlipperAppType.EXTERNAL,
entry_point="totp_app",
@@ -101,7 +101,7 @@ bool totp_scene_app_settings_handle_event(
}
SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
if(event->input.type != InputTypePress) {
if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) {
return true;
}
@@ -1,6 +1,6 @@
#include "totp_scene_authenticate.h"
#include <dialogs/dialogs.h>
#include <Authenticator_icons.h>
#include <totp_icons.h>
#include "../../types/common.h"
#include "../../services/ui/constants.h"
#include "../../services/config/config.h"
@@ -1,7 +1,7 @@
#include <gui/gui.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <Authenticator_icons.h>
#include <totp_icons.h>
#include "totp_scene_generate_token.h"
#include "../../types/token_info.h"
#include "../../types/common.h"
@@ -293,7 +293,7 @@ bool totp_scene_generate_token_handle_event(
return true;
}
if(event->input.type != InputTypePress) {
if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) {
return true;
}
@@ -5,12 +5,46 @@
#include "../types/plugin_event.h"
#include "totp_scenes_enum.h"
/**
* @brief Activates scene
* @param plugin_state application state
* @param scene scene to be activated
* @param context scene context to be passed to the scene activation method
*/
void totp_scene_director_activate_scene(
PluginState* const plugin_state,
Scene scene,
const void* context);
/**
* @brief Deactivate current scene
* @param plugin_state application state
*/
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state);
/**
* @brief Initializes all the available scenes
* @param plugin_state application state
*/
void totp_scene_director_init_scenes(PluginState* const plugin_state);
/**
* @brief Renders current scene
* @param canvas canvas to render at
* @param plugin_state application state
*/
void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state);
/**
* @brief Disposes all the available scenes
* @param plugin_state application state
*/
void totp_scene_director_dispose(const PluginState* const plugin_state);
/**
* @brief Handles application event for the current scene
* @param event event to be handled
* @param plugin_state application state
* @return \c true if event handled and applilcation should continue; \c false if application should be closed
*/
bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state);
@@ -147,13 +147,14 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
dialog_message_free(message);
if(dialog_result == DialogMessageButtonRight &&
!scene_state->current_token_index.is_null) {
ListNode* list_node = list_element_at(
plugin_state->tokens_list, scene_state->current_token_index.value);
TokenInfo* tokenInfo = list_node->data;
token_info_free(tokenInfo);
plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node);
TokenInfo* tokenInfo = NULL;
plugin_state->tokens_list = list_remove_at(
plugin_state->tokens_list,
scene_state->current_token_index.value,
(void**)&tokenInfo);
plugin_state->tokens_count--;
furi_check(tokenInfo != NULL);
token_info_free(tokenInfo);
totp_full_save_config_file(plugin_state);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
@@ -1,10 +1,36 @@
#pragma once
/**
* @brief TOTP application scenes
*/
typedef enum {
/**
* @brief Empty scene which does nothing
*/
TotpSceneNone,
/**
* @brief Scene where user have to enter PIN to authenticate
*/
TotpSceneAuthentication,
/**
* @brief Scene where actual TOTP token is getting generated and displayed to the user
*/
TotpSceneGenerateToken,
/**
* @brief Scene where user can add new token
*/
TotpSceneAddNewToken,
/**
* @brief Scene with a menu for given token, allowing user to do multiple actions
*/
TotpSceneTokenMenu,
/**
* @brief Scene where user can change application settings
*/
TotpSceneAppSettings
} Scene;
@@ -15,8 +15,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "base32.h"
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
@@ -29,5 +29,11 @@
#include <stdint.h>
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize)
__attribute__((visibility("hidden")));
/**
* @brief Decodes Base-32 encoded bytes into plain bytes.
* @param encoded Base-32 encoded bytes
* @param[out] result result output buffer
* @param bufSize result output buffer size
* @return Decoded result length in bytes if successfully decoded; \c -1 otherwise
*/
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize);
@@ -8,6 +8,7 @@
#include "commands/delete/delete.h"
#include "commands/timezone/timezone.h"
#include "commands/help/help.h"
#include "commands/move/move.h"
static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
TOTP_CLI_PRINTF(
@@ -44,6 +45,10 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_TIMEZONE) == 0 ||
furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_TIMEZONE_ALT) == 0) {
totp_cli_command_timezone_handle(plugin_state, args, cli);
} else if(
furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_MOVE) == 0 ||
furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_MOVE_ALT) == 0) {
totp_cli_command_move_handle(plugin_state, args, cli);
} else {
totp_cli_print_unknown_command(cmd);
}
@@ -175,6 +175,8 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) == 0) {
mask_user_input = false;
parsed = true;
} else {
TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str));
}
if(!parsed) {
@@ -4,6 +4,7 @@
#include "../delete/delete.h"
#include "../list/list.h"
#include "../timezone/timezone.h"
#include "../move/move.h"
void totp_cli_command_help_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT
@@ -23,6 +24,7 @@ void totp_cli_command_help_handle() {
totp_cli_command_add_docopt_usage();
totp_cli_command_delete_docopt_usage();
totp_cli_command_timezone_docopt_usage();
totp_cli_command_move_docopt_usage();
cli_nl();
TOTP_CLI_PRINTF("Commands:\r\n");
totp_cli_command_help_docopt_commands();
@@ -30,6 +32,7 @@ void totp_cli_command_help_handle() {
totp_cli_command_add_docopt_commands();
totp_cli_command_delete_docopt_commands();
totp_cli_command_timezone_docopt_commands();
totp_cli_command_move_docopt_commands();
cli_nl();
TOTP_CLI_PRINTF("Arguments:\r\n");
totp_cli_command_add_docopt_arguments();
@@ -39,4 +42,5 @@ void totp_cli_command_help_handle() {
TOTP_CLI_PRINTF("Options:\r\n");
totp_cli_command_add_docopt_options();
totp_cli_command_delete_docopt_options();
totp_cli_command_move_docopt_options();
}
@@ -0,0 +1,164 @@
#include "move.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../list/list.h"
#include "../../../../types/token_info.h"
#include "../../../config/config.h"
#include "../../cli_helpers.h"
#include "../../../../scenes/scene_director.h"
#define TOTP_CLI_COMMAND_MOVE_ARG_INDEX "index"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME "name"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX "-n"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "index"
#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX "-i"
void totp_cli_command_move_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE ", " TOTP_CLI_COMMAND_MOVE_ALT
" Move\\rename token\r\n");
}
void totp_cli_command_move_docopt_usage() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NAME
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_MOVE " | " TOTP_CLI_COMMAND_MOVE_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_INDEX) " " DOCOPT_OPTIONAL(
DOCOPT_OPTION(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX))) "\r\n");
}
void totp_cli_command_move_docopt_options() {
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX,
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME)) " New token name.\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX,
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX)) " New token index.\r\n");
}
void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
int token_index;
if(!args_read_int_and_trim(args, &token_index)) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
return;
}
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
return;
}
if(token_index < 1 || token_index > plugin_state->tokens_count) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
return;
}
FuriString* temp_str = furi_string_alloc();
char* new_token_name = NULL;
int new_token_index = 0;
while(args_read_string_and_trim(args, temp_str)) {
bool parsed = false;
if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX) == 0) {
if(!args_read_string_and_trim(args, temp_str)) {
TOTP_CLI_PRINTF(
"Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX
"\"\r\n");
} else {
if(new_token_name != NULL) {
free(new_token_name);
}
new_token_name = malloc(furi_string_size(temp_str) + 1);
if(new_token_name == NULL) {
furi_string_free(temp_str);
return;
}
strlcpy(
new_token_name,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str) + 1);
parsed = true;
}
} else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX) == 0) {
if(!args_read_int_and_trim(args, &new_token_index)) {
TOTP_CLI_PRINTF(
"Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX
"\"\r\n");
} else if(new_token_index < 1 || new_token_index > plugin_state->tokens_count) {
TOTP_CLI_PRINTF(
"\"%" PRId16
"\" is incorrect value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX
"\"\r\n",
new_token_index);
} else {
parsed = true;
}
} else {
TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str));
}
if(!parsed) {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
furi_string_free(temp_str);
if(new_token_name != NULL) {
free(new_token_name);
}
return;
}
}
if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
furi_string_free(temp_str);
if(new_token_name != NULL) {
free(new_token_name);
}
return;
}
bool activate_generate_token_scene = false;
if(plugin_state->current_scene != TotpSceneAuthentication) {
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
activate_generate_token_scene = true;
}
bool token_updated = false;
TokenInfo* token_info = NULL;
if(new_token_index > 0) {
plugin_state->tokens_list =
list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info);
furi_check(token_info != NULL);
plugin_state->tokens_list =
list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info);
token_updated = true;
} else {
token_info = list_element_at(plugin_state->tokens_list, token_index - 1)->data;
}
if(new_token_name != NULL) {
free(token_info->name);
token_info->name = new_token_name;
token_updated = true;
}
if(token_updated) {
totp_full_save_config_file(plugin_state);
}
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
if(token_updated) {
TOTP_CLI_PRINTF("Token \"%s\" has been successfully updated\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
}
furi_string_free(temp_str);
}
@@ -0,0 +1,12 @@
#pragma once
#include <cli/cli.h>
#include "../../../../types/plugin_state.h"
#define TOTP_CLI_COMMAND_MOVE "move"
#define TOTP_CLI_COMMAND_MOVE_ALT "mv"
void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli);
void totp_cli_command_move_docopt_commands();
void totp_cli_command_move_docopt_usage();
void totp_cli_command_move_docopt_options();
@@ -6,18 +6,77 @@
#include "../../types/token_info.h"
#include "constants.h"
/**
* @brief Token loading results
*/
typedef enum {
/**
* @brief All the tokens loaded successfully
*/
TokenLoadingResultSuccess,
/**
* @brief All the tokens loaded, but there are some warnings
*/
TokenLoadingResultWarning,
/**
* @brief Tokens not loaded because of error(s)
*/
TokenLoadingResultError
} TokenLoadingResult;
/**
* @brief Opens storage record
* @return Storage record
*/
Storage* totp_open_storage();
/**
* @brief Closes storage record
*/
void totp_close_storage();
/**
* @brief Opens or creates TOTP application standard config file
* @param storage storage record to use
* @return Config file reference
*/
FlipperFormat* totp_open_config_file(Storage* storage);
/**
* @brief Closes config file
* @param file config file reference
*/
void totp_close_config_file(FlipperFormat* file);
/**
* @brief Saves all the settings and tokens to an application config file
* @param plugin_state application state
*/
void totp_full_save_config_file(const PluginState* const plugin_state);
/**
* @brief Loads basic information from an application config file into application state without loading all the tokens
* @param plugin_state application state
*/
void totp_config_file_load_base(PluginState* const plugin_state);
/**
* @brief Loads tokens from an application config file into application state
* @param plugin_state application state
* @return Results of the loading
*/
TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state);
/**
* @brief Add new token to the end of the application config file
* @param token_info token information to be saved
*/
void totp_config_file_save_new_token(const TokenInfo* token_info);
/**
* @brief Updates timezone offset in an application config file
* @param new_timezone_offset new timezone offset to be set
*/
void totp_config_file_update_timezone_offset(float new_timezone_offset);
@@ -2,15 +2,45 @@
#include "../../types/plugin_state.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[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,
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[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,
size_t* decrypted_data_length);
/**
* @brief Seed initialization vector (IV) using user's PIN
* @param plugin_state application state
* @param pin user's PIN
* @param pin_length user's PIN length
*/
void totp_crypto_seed_iv(PluginState* plugin_state, 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
* @return \c true if cryptographic information is valid; \c false otherwise
*/
bool totp_crypto_verify_key(const PluginState* plugin_state);
@@ -2,7 +2,6 @@
#include <errno.h>
#include <stdint.h>
#include <string.h>
#ifndef _RSIZE_T_DECLARED
typedef uint64_t rsize_t;
@@ -13,4 +12,12 @@ typedef int16_t errno_t; //-V677
#define _ERRNOT_DECLARED
#endif
/**
* @brief Copies the value \p c into each of the first \p n characters of the object pointed to by \p s.
* @param s pointer to the object to fill
* @param smax size of the destination object
* @param c fill byte
* @param n number of bytes to fill
* @return \c 0 on success; non-zero otherwise
*/
errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n);
@@ -1,6 +1,6 @@
#include "hid_worker.h"
const uint8_t hid_number_keys[10] = {
static const uint8_t hid_number_keys[10] = {
HID_KEYBOARD_0,
HID_KEYBOARD_1,
HID_KEYBOARD_2,
@@ -19,6 +19,10 @@ static void totp_hid_worker_restore_usb_mode(TotpHidWorkerTypeContext* context)
}
}
static inline bool totp_hid_worker_stop_requested() {
return furi_thread_flags_get() & TotpHidWorkerEvtStop;
}
static void totp_hid_worker_type_code(TotpHidWorkerTypeContext* context) {
context->usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_unlock();
@@ -27,11 +31,11 @@ static void totp_hid_worker_type_code(TotpHidWorkerTypeContext* context) {
do {
furi_delay_ms(500);
i++;
} while(!furi_hal_hid_is_connected() && i < 50);
furi_delay_ms(500);
} while(!furi_hal_hid_is_connected() && i < 100 && !totp_hid_worker_stop_requested());
if(furi_hal_hid_is_connected() &&
furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) {
furi_delay_ms(500);
i = 0;
while(i < context->string_length && context->string[i] != 0) {
uint8_t digit = context->string[i] - '0';
@@ -91,7 +95,7 @@ TotpHidWorkerTypeContext* totp_hid_worker_start() {
}
void totp_hid_worker_stop(TotpHidWorkerTypeContext* context) {
furi_assert(context);
furi_assert(context != NULL);
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpHidWorkerEvtStop);
furi_thread_join(context->thread);
furi_thread_free(context->thread);
@@ -101,6 +105,6 @@ void totp_hid_worker_stop(TotpHidWorkerTypeContext* context) {
}
void totp_hid_worker_notify(TotpHidWorkerTypeContext* context, TotpHidWorkerEvtFlags event) {
furi_assert(context);
furi_assert(context != NULL);
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
}
@@ -2,5 +2,16 @@
#include <stdint.h>
/**
* @brief Swap bytes in 32-bit value
* @param val value to swap bytes in
* @return Value with bytes swapped
*/
uint32_t swap_uint32(uint32_t val);
/**
* @brief Swap bytes in 64-bit value
* @param val value to swap bytes in
* @return Value with bytes swapped
*/
uint64_t swap_uint64(uint64_t val);
+58 -1
View File
@@ -29,7 +29,7 @@ ListNode* list_add(ListNode* head, void* data) {
}
ListNode* list_find(ListNode* head, const void* data) {
ListNode* it;
ListNode* it = NULL;
for(it = head; it != NULL; it = it->next)
if(it->data == data) break;
@@ -67,6 +67,63 @@ ListNode* list_remove(ListNode* head, ListNode* ep) {
return head;
}
ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data) {
if(head == NULL) {
return NULL;
}
ListNode* it;
ListNode* prev = NULL;
uint16_t i;
for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++)
;
if(it == NULL) return head;
ListNode* new_head = head;
if(prev == NULL) {
new_head = it->next;
} else {
prev->next = it->next;
}
if(removed_node_data != NULL) {
*removed_node_data = it->data;
}
free(it);
return new_head;
}
ListNode* list_insert_at(ListNode* head, uint16_t index, void* data) {
if(index == 0 || head == NULL) {
ListNode* new_head = list_init_head(data);
if(new_head != NULL) {
new_head->next = head;
}
return new_head;
}
ListNode* it;
ListNode* prev = NULL;
uint16_t i;
for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++)
;
ListNode* new = malloc(sizeof(ListNode));
if(new == NULL) return NULL;
new->data = data;
new->next = it;
prev->next = new;
return head;
}
void list_free(ListNode* head) {
ListNode* it = head;
ListNode* tmp;
+70 -10
View File
@@ -3,25 +3,85 @@
#include <stdlib.h>
#include <inttypes.h>
/**
* @brief Single linked list node
*/
typedef struct ListNode {
/**
* @brief Pointer to the data assigned to the current list node
*/
void* data;
/**
* @brief Pointer to the next list node
*/
struct ListNode* next;
} ListNode;
/**
* @brief Initializes a new list node head
* @param data data to be assigned to the head list node
* @return Head list node
*/
ListNode* list_init_head(void* data);
/**
* @brief Adds new list node to the end of the list
* @param head head list node
* @param data data to be assigned to the newly added list node
* @return Head list node
*/
ListNode* list_add(
ListNode* head,
void* data); /* adds element with specified data to the end of the list and returns new head node. */
ListNode* list_find(
ListNode* head,
const void* data); /* returns pointer of element with specified data in list. */
ListNode* list_element_at(
ListNode* head,
uint16_t index); /* returns pointer of element with specified index in list. */
ListNode* list_remove(
ListNode* head,
ListNode* ep); /* removes element from the list and returns new head node. */
void list_free(ListNode* head); /* deletes all elements of the list. */
/**
* @brief Searches list node with the given assigned \p data in the list
* @param head head list node
* @param data data to be searched
* @return List node containing \p data if there is such a node in the list; \c NULL otherwise
*/
ListNode* list_find(ListNode* head, const void* data);
/**
* @brief Searches list node with the given \p index in the list
* @param head head list node
* @param index desired list node index
* @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise
*/
ListNode* list_element_at(ListNode* head, uint16_t index);
/**
* @brief Removes list node from the list
* @param head head list node
* @param ep list node to be removed
* @return Head list node
*/
ListNode* list_remove(ListNode* head, ListNode* ep);
/**
* @brief Removes list node with the given \p index in the list from the list
* @param head head list node
* @param index index of the node to be removed
* @param[out] removed_node_data data which was assigned to the removed list node
* @return Head list node
*/
ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data);
/**
* @brief Inserts new list node at the given index
* @param head head list node
* @param index index in the list where the new list node should be inserted
* @param data data to be assgned to the new list node
* @return Head list node
*/
ListNode* list_insert_at(ListNode* head, uint16_t index, void* data);
/**
* @brief Disposes all the list nodes in the list
* @param head head list node
*/
void list_free(ListNode* head);
#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
do { \
@@ -2,7 +2,17 @@
#include <stdint.h>
typedef enum { RollOverflowBehaviorStop, RollOverflowBehaviorRoll } TotpRollValueOverflowBehavior;
typedef enum {
/**
* @brief Do not change value if it reached constraint
*/
RollOverflowBehaviorStop,
/**
* @brief Set value to opposite constraint value if it reached constraint
*/
RollOverflowBehaviorRoll
} TotpRollValueOverflowBehavior;
#define TOTP_ROLL_VALUE_FN_HEADER(type, step_type) \
void totp_roll_value_##type( \
@@ -12,6 +22,35 @@ typedef enum { RollOverflowBehaviorStop, RollOverflowBehaviorRoll } TotpRollValu
type max, \
TotpRollValueOverflowBehavior overflow_behavior)
/**
* @brief Rolls \c int8_t \p value using \p min and \p max as an value constraints with \p step step.
* When value reaches constraint value \p overflow_behavior defines what to do next.
* @param[in,out] value value to roll
* @param step step to be used to change value
* @param min minimal possible value
* @param max maximum possible value
* @param overflow_behavior defines what to do when value reaches constraint value
*/
TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t);
/**
* @brief Rolls \c uint8_t \p value using \p min and \p max as an value constraints with \p step step.
* When value reaches constraint value \p overflow_behavior defines what to do next.
* @param[in,out] value value to roll
* @param step step to be used to change value
* @param min minimal possible value
* @param max maximum possible value
* @param overflow_behavior defines what to do when value reaches constraint value
*/
TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t);
/**
* @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step.
* When value reaches constraint value \p overflow_behavior defines what to do next.
* @param[in,out] value value to roll
* @param step step to be used to change value
* @param min minimal possible value
* @param max maximum possible value
* @param overflow_behavior defines what to do when value reaches constraint value
*/
TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t);
@@ -2,5 +2,17 @@
#include <inttypes.h>
/**
* @brief Calculates timezone offset in seconds given timezone offset in hours.
* @param hours timezone offset in hours
* @return Timezone offset in seconds.
*/
int32_t timezone_offset_from_hours(float hours);
/**
* @brief Applies timezone offset to a given time.
* @param time time to apply offset to.
* @param offset timezone offset in seconds.
* @return Time with timezone offset applied.
*/
uint64_t timezone_offset_apply(uint64_t time, int32_t offset);
+23 -40
View File
@@ -11,48 +11,43 @@
#include "../hmac/byteswap.h"
#include "../timezone_utils/timezone_utils.h"
/*
Generates the timeblock for a time in seconds.
Timeblocks are the amount of intervals in a given time. For example,
if 1,000,000 seconds has passed for 30 second intervals, you would get
33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds.
for_time is a time in seconds to get the current timeblocks
Returns
timeblock given for_time, using data->interval
error, 0
*/
#define HMAC_MAX_SIZE 64
/**
* @brief Generates the timeblock for a time in seconds.
* Timeblocks are the amount of intervals in a given time. For example,
* if 1,000,000 seconds has passed for 30 second intervals, you would get
* 33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds.
* @param interval in seconds
* @param for_time a time in seconds to get the current timeblocks
* @return Timeblock given \p for_time using \p interval
*/
uint64_t totp_timecode(uint8_t interval, uint64_t for_time) {
return for_time / interval;
}
/*
Generates an OTP (One Time Password).
input is a number used to generate the OTP
out_str is the null-terminated output string already allocated
Returns
OTP code if otp code was successfully generated
0 otherwise
*/
/**
* @brief Generates an OTP (One Time Password)
* @param algo hashing algorithm to be used
* @param digits desired TOTP code length
* @param plain_secret plain token secret
* @param plain_secret_length plain token secret length
* @param input input data for OTP code generation
* @return OTP code if code was successfully generated; 0 otherwise
*/
uint32_t otp_generate(
TOTP_ALGO algo,
uint8_t digits,
const uint8_t* plain_secret,
size_t plain_secret_length,
uint64_t input) {
uint8_t* hmac = malloc(64);
if(hmac == NULL) return OTP_ERROR;
memset(hmac, 0, 64);
uint8_t hmac[HMAC_MAX_SIZE] = {0};
uint64_t input_swapped = swap_uint64(input);
int hmac_len = (*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, hmac);
int hmac_len =
(*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, &hmac[0]);
if(hmac_len == 0) {
free(hmac);
return OTP_ERROR;
}
@@ -62,21 +57,9 @@ uint32_t otp_generate(
(hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
i_code %= (uint64_t)pow(10, digits);
free(hmac);
return i_code;
}
/*
Generates a OTP key using the totp algorithm.
for_time is the time the generated key will be created for
offset is a timeblock adjustment for the generated key
out_str is the null-terminated output string already allocated
Returns
TOTP code if otp code was successfully generated
0 otherwise
*/
uint32_t totp_at(
TOTP_ALGO algo,
uint8_t digits,
+29 -25
View File
@@ -5,16 +5,15 @@
#define OTP_ERROR (0)
/*
Must compute HMAC using passed arguments,
output as char array through output.
key is secret key.
input is input number.
output is an output buffer of the resulting HMAC operation.
Must return 0 if error, or the length in bytes of the HMAC operation.
*/
/**
* @brief Must compute HMAC using passed arguments, output as char array through output.
* \p key is secret key buffer.
* \p key_length is secret key buffer length.
* \p input is input buffer.
* \p input_length is input buffer length.
* \p output is an output buffer of the resulting HMAC operation.
* Must return 0 if error, or the length in bytes of the HMAC operation.
*/
typedef int (*TOTP_ALGO)(
const uint8_t* key,
size_t key_length,
@@ -22,27 +21,32 @@ typedef int (*TOTP_ALGO)(
size_t input_length,
uint8_t* output);
/*
Computes HMAC using SHA1
*/
/**
* @brief Computes HMAC using SHA1
*/
extern const TOTP_ALGO TOTP_ALGO_SHA1;
/*
Computes HMAC using SHA256
*/
/**
* @brief Computes HMAC using SHA256
*/
extern const TOTP_ALGO TOTP_ALGO_SHA256;
/*
Computes HMAC using SHA512
*/
/**
* @brief Computes HMAC using SHA512
*/
extern const TOTP_ALGO TOTP_ALGO_SHA512;
/*
Computes TOTP token
Returns:
TOTP token on success
0 otherwise
*/
/**
* @brief Generates a OTP key using the totp algorithm.
* @param algo hashing algorithm to be used
* @param digits desired TOTP code length
* @param plain_secret plain token secret
* @param plain_secret_length plain token secret length
* @param for_time the time the generated key will be created for
* @param timezone UTC timezone adjustment for the generated key
* @param interval token lifetime in seconds
* @return TOTP code if code was successfully generated; 0 otherwise
*/
uint32_t totp_at(
TOTP_ALGO algo,
uint8_t digits,
@@ -1,5 +1,5 @@
#include "ui_controls.h"
#include <Authenticator_icons.h>
#include <totp_icons.h>
#include "constants.h"
#define TEXT_BOX_HEIGHT 13
@@ -3,11 +3,29 @@
#include <inttypes.h>
#include <gui/gui.h>
/**
* @brief Renders TextBox control
* @param canvas canvas to render control at
* @param y vertical position of a control to be rendered at
* @param text text to be rendered inside control
* @param is_selected whether control should be rendered as focused or not
*/
void ui_control_text_box_render(
Canvas* const canvas,
int16_t y,
const char* text,
bool is_selected);
/**
* @brief Renders Button control
* @param canvas canvas to render control at
* @param x horizontal position of a control to be rendered at
* @param y vertical position of a control to be rendered at
* @param width control width
* @param height control height
* @param text text to be rendered inside control
* @param is_selected whether control should be rendered as focused or not
*/
void ui_control_button_render(
Canvas* const canvas,
int16_t x,
@@ -16,6 +34,16 @@ void ui_control_button_render(
uint8_t height,
const char* text,
bool is_selected);
/**
* @brief Renders Select control
* @param canvas canvas to render control at
* @param x horizontal position of a control to be rendered at
* @param y vertical position of a control to be rendered at
* @param width control width
* @param text text to be rendered inside control
* @param is_selected whether control should be rendered as focused or not
*/
void ui_control_select_render(
Canvas* const canvas,
int16_t x,
+14 -30
View File
@@ -24,7 +24,7 @@
static void render_callback(Canvas* const canvas, void* ctx) {
PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
if(plugin_state != NULL && !plugin_state->changing_scene) {
if (plugin_state != NULL && !plugin_state->changing_scene) {
totp_scene_director_render(canvas, plugin_state);
}
@@ -49,43 +49,29 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
totp_scene_director_init_scenes(plugin_state);
if(plugin_state->crypto_verify_data == NULL) {
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);
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, message);
dialog_message_free(message);
if(dialog_result == DialogMessageButtonRight) {
if (dialog_result == DialogMessageButtonRight) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
} else {
totp_crypto_seed_iv(plugin_state, NULL, 0);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
} else if(plugin_state->pin_set) {
} else if (plugin_state->pin_set) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
} else {
totp_crypto_seed_iv(plugin_state, NULL, 0);
if(totp_crypto_verify_key(plugin_state)) {
if (totp_crypto_verify_key(plugin_state)) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} 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");
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_set_text(message, "Digital signature verification failed", SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter);
dialog_message_show(plugin_state->dialogs, message);
dialog_message_free(message);
return false;
@@ -108,7 +94,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
ListNode* node = plugin_state->tokens_list;
ListNode* tmp;
while(node != NULL) {
while (node != NULL) {
tmp = node->next;
TokenInfo* tokenInfo = node->data;
token_info_free(tokenInfo);
@@ -116,7 +102,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
node = tmp;
}
if(plugin_state->crypto_verify_data != NULL) {
if (plugin_state->crypto_verify_data != NULL) {
free(plugin_state->crypto_verify_data);
}
free(plugin_state);
@@ -127,7 +113,7 @@ int32_t totp_app() {
PluginState* plugin_state = malloc(sizeof(PluginState));
furi_check(plugin_state != NULL);
if(!totp_plugin_state_init(plugin_state)) {
if (!totp_plugin_state_init(plugin_state)) {
FURI_LOG_E(LOGGING_TAG, "App state initialization failed\r\n");
totp_plugin_state_free(plugin_state);
return 254;
@@ -152,20 +138,18 @@ int32_t totp_app() {
bool processing = true;
uint32_t last_user_interaction_time = furi_get_tick();
while(processing) {
if(plugin_state->changing_scene) continue;
if (plugin_state->changing_scene) continue;
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
PluginState* plugin_state_m = acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
if (event.type == EventTypeKey) {
last_user_interaction_time = furi_get_tick();
}
processing = totp_scene_director_handle_event(&event, plugin_state_m);
} else if(
plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication &&
furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) {
} else if (plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) {
totp_scene_director_activate_scene(plugin_state_m, TotpSceneAuthentication, NULL);
}
@@ -8,22 +8,82 @@
#define TOTP_IV_SIZE 16
/**
* @brief Application state structure
*/
typedef struct {
/**
* @brief Application current scene
*/
Scene current_scene;
/**
* @brief Application current scene state
*/
void* current_scene_state;
/**
* @brief Whether scene is changing now
*/
bool changing_scene;
/**
* @brief Reference to the firmware notification subsystem
*/
NotificationApp* notification;
/**
* @brief Reference to the firmware dialogs subsystem
*/
DialogsApp* dialogs;
/**
* @brief Reference to the firmware GUI subsystem
*/
Gui* gui;
/**
* @brief Timezone UTC offset in hours
*/
float timezone_offset;
/**
* @brief Token list head node
*/
ListNode* tokens_list;
/**
* @brief Whether token list is loaded or not
*/
bool token_list_loaded;
/**
* @brief Tokens list length
*/
uint16_t tokens_count;
/**
* @brief Encrypted well-known string data
*/
uint8_t* crypto_verify_data;
/**
* @brief Encrypted well-known string 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[TOTP_IV_SIZE];
/**
* @brief Basic randomly-generated initialization vector (IV)
*/
uint8_t base_iv[TOTP_IV_SIZE];
} PluginState;
+79 -2
View File
@@ -2,25 +2,102 @@
#include <inttypes.h>
typedef enum { SHA1, SHA256, SHA512 } TokenHashAlgo;
/**
* @brief Hashing algorithm to be used to generate token
*/
typedef enum {
/**
* @brief SHA1 hashing algorithm
*/
SHA1,
typedef enum { TOTP_6_DIGITS, TOTP_8_DIGITS } TokenDigitsCount;
/**
* @brief SHA256 hashing algorithm
*/
SHA256,
/**
* @brief SHA512 hashing algorithm
*/
SHA512
} TokenHashAlgo;
/**
* @brief Token digits count to be generated.
*/
typedef enum {
/**
* @brief 6 digits
*/
TOTP_6_DIGITS,
/**
* @brief 8 digits
*/
TOTP_8_DIGITS
} TokenDigitsCount;
#define TOTP_TOKEN_DIGITS_MAX_COUNT 8
/**
* @brief TOTP token information
*/
typedef struct {
/**
* @brief Encrypted token secret
*/
uint8_t* token;
/**
* @brief Encrypted token secret length
*/
size_t token_length;
/**
* @brief User-friendly token name
*/
char* name;
/**
* @brief Hashing algorithm
*/
TokenHashAlgo algo;
/**
* @brief Desired TOTP token length
*/
TokenDigitsCount digits;
} TokenInfo;
/**
* @brief Allocates a new instance of \c TokenInfo
* @return
*/
TokenInfo* token_info_alloc();
/**
* @brief Disposes all the resources allocated by the given \c TokenInfo instance
* @param token_info instance to be disposed
*/
void token_info_free(TokenInfo* token_info);
/**
* @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo
* @param token_info instance where secret should be updated
* @param base32_token_secret plain token secret in Base32 format
* @param token_secret_length plain token secret length
* @param iv initialization vecor (IV) to be used for encryption
* @return \c true if token successfully set; \c false otherwise
*/
bool token_info_set_secret(
TokenInfo* token_info,
const char* base32_token_secret,
size_t token_secret_length,
const uint8_t* iv);
/**
* @brief Gets token digits count as \c uint8_t type
* @param token_info instance which's desired digits count should be returned
* @return Token digits length as \c uint8_t type
*/
uint8_t token_info_get_digits_count(const TokenInfo* token_info);