|
@@ -12,6 +12,8 @@
|
|
|
|
|
|
|
|
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"};
|
|
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"};
|
|
|
char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"};
|
|
char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"};
|
|
|
|
|
+char* TOKEN_TYPE_LIST[] = {"Time-based (TOTP)", "Counter-based (HOTP)"};
|
|
|
|
|
+
|
|
|
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {
|
|
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {
|
|
|
TokenDigitsCountFive,
|
|
TokenDigitsCountFive,
|
|
|
TokenDigitsCountSix,
|
|
TokenDigitsCountSix,
|
|
@@ -20,9 +22,10 @@ TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {
|
|
|
typedef enum {
|
|
typedef enum {
|
|
|
TokenNameTextBox,
|
|
TokenNameTextBox,
|
|
|
TokenSecretTextBox,
|
|
TokenSecretTextBox,
|
|
|
|
|
+ TokenTypeSelect,
|
|
|
TokenAlgoSelect,
|
|
TokenAlgoSelect,
|
|
|
TokenLengthSelect,
|
|
TokenLengthSelect,
|
|
|
- TokenDurationSelect,
|
|
|
|
|
|
|
+ TokenDurationOrCounterSelect,
|
|
|
ConfirmButton,
|
|
ConfirmButton,
|
|
|
} Control;
|
|
} Control;
|
|
|
|
|
|
|
@@ -37,7 +40,10 @@ typedef struct {
|
|
|
TokenHashAlgo algo;
|
|
TokenHashAlgo algo;
|
|
|
uint8_t digits_count_index;
|
|
uint8_t digits_count_index;
|
|
|
uint8_t duration;
|
|
uint8_t duration;
|
|
|
|
|
+ char* initial_counter;
|
|
|
|
|
+ size_t initial_counter_length;
|
|
|
FuriString* duration_text;
|
|
FuriString* duration_text;
|
|
|
|
|
+ TokenType type;
|
|
|
} SceneState;
|
|
} SceneState;
|
|
|
|
|
|
|
|
struct TotpAddContext {
|
|
struct TotpAddContext {
|
|
@@ -45,7 +51,10 @@ struct TotpAddContext {
|
|
|
const CryptoSettings* crypto_settings;
|
|
const CryptoSettings* crypto_settings;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 };
|
|
|
|
|
|
|
+enum TotpIteratorUpdateTokenResultsEx {
|
|
|
|
|
+ TotpIteratorUpdateTokenResultInvalidSecret = 1,
|
|
|
|
|
+ TotpIteratorUpdateTokenResultInvalidCounter = 2
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
static void update_duration_text(SceneState* scene_state) {
|
|
static void update_duration_text(SceneState* scene_state) {
|
|
|
furi_string_printf(scene_state->duration_text, "%d sec.", scene_state->duration);
|
|
furi_string_printf(scene_state->duration_text, "%d sec.", scene_state->duration);
|
|
@@ -53,6 +62,10 @@ static void update_duration_text(SceneState* scene_state) {
|
|
|
|
|
|
|
|
static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, const void* context) {
|
|
static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, const void* context) {
|
|
|
const struct TotpAddContext* context_t = context;
|
|
const struct TotpAddContext* context_t = context;
|
|
|
|
|
+ if(sscanf(context_t->scene_state->initial_counter, "%" PRIu64, &tokenInfo->counter) != 1) {
|
|
|
|
|
+ return TotpIteratorUpdateTokenResultInvalidCounter;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if(!token_info_set_secret(
|
|
if(!token_info_set_secret(
|
|
|
tokenInfo,
|
|
tokenInfo,
|
|
|
context_t->scene_state->token_secret,
|
|
context_t->scene_state->token_secret,
|
|
@@ -69,6 +82,7 @@ static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, con
|
|
|
tokenInfo->algo = context_t->scene_state->algo;
|
|
tokenInfo->algo = context_t->scene_state->algo;
|
|
|
tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[context_t->scene_state->digits_count_index];
|
|
tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[context_t->scene_state->digits_count_index];
|
|
|
tokenInfo->duration = context_t->scene_state->duration;
|
|
tokenInfo->duration = context_t->scene_state->duration;
|
|
|
|
|
+ tokenInfo->type = context_t->scene_state->type;
|
|
|
|
|
|
|
|
return TotpIteratorUpdateTokenResultSuccess;
|
|
return TotpIteratorUpdateTokenResultSuccess;
|
|
|
}
|
|
}
|
|
@@ -93,6 +107,33 @@ static void ask_user_input(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static void update_screen_y_offset(SceneState* scene_state) {
|
|
|
|
|
+ if(scene_state->selected_control > TokenLengthSelect) {
|
|
|
|
|
+ scene_state->screen_y_offset = 68;
|
|
|
|
|
+ } else if(scene_state->selected_control > TokenAlgoSelect) {
|
|
|
|
|
+ scene_state->screen_y_offset = 51;
|
|
|
|
|
+ } else if(scene_state->selected_control > TokenTypeSelect) {
|
|
|
|
|
+ scene_state->screen_y_offset = 34;
|
|
|
|
|
+ } else if(scene_state->selected_control > TokenSecretTextBox) {
|
|
|
|
|
+ scene_state->screen_y_offset = 17;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ scene_state->screen_y_offset = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void
|
|
|
|
|
+ show_invalid_field_message(const PluginState* plugin_state, Control control, const char* text) {
|
|
|
|
|
+ DialogMessage* message = dialog_message_alloc();
|
|
|
|
|
+ SceneState* scene_state = plugin_state->current_scene_state;
|
|
|
|
|
+ dialog_message_set_buttons(message, "Back", NULL, NULL);
|
|
|
|
|
+ dialog_message_set_text(
|
|
|
|
|
+ message, text, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter);
|
|
|
|
|
+ dialog_message_show(plugin_state->dialogs_app, message);
|
|
|
|
|
+ dialog_message_free(message);
|
|
|
|
|
+ scene_state->selected_control = control;
|
|
|
|
|
+ update_screen_y_offset(scene_state);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void totp_scene_add_new_token_activate(PluginState* plugin_state) {
|
|
void totp_scene_add_new_token_activate(PluginState* plugin_state) {
|
|
|
SceneState* scene_state = malloc(sizeof(SceneState));
|
|
SceneState* scene_state = malloc(sizeof(SceneState));
|
|
|
furi_check(scene_state != NULL);
|
|
furi_check(scene_state != NULL);
|
|
@@ -101,6 +142,8 @@ void totp_scene_add_new_token_activate(PluginState* plugin_state) {
|
|
|
scene_state->token_name_length = strlen(scene_state->token_name);
|
|
scene_state->token_name_length = strlen(scene_state->token_name);
|
|
|
scene_state->token_secret = "Secret";
|
|
scene_state->token_secret = "Secret";
|
|
|
scene_state->token_secret_length = strlen(scene_state->token_secret);
|
|
scene_state->token_secret_length = strlen(scene_state->token_secret);
|
|
|
|
|
+ scene_state->initial_counter = "Counter";
|
|
|
|
|
+ scene_state->initial_counter_length = strlen(scene_state->initial_counter);
|
|
|
|
|
|
|
|
scene_state->screen_y_offset = 0;
|
|
scene_state->screen_y_offset = 0;
|
|
|
|
|
|
|
@@ -108,6 +151,7 @@ void totp_scene_add_new_token_activate(PluginState* plugin_state) {
|
|
|
|
|
|
|
|
scene_state->duration = TokenDurationDefault;
|
|
scene_state->duration = TokenDurationDefault;
|
|
|
scene_state->duration_text = furi_string_alloc();
|
|
scene_state->duration_text = furi_string_alloc();
|
|
|
|
|
+ scene_state->type = TokenTypeTOTP;
|
|
|
update_duration_text(scene_state);
|
|
update_duration_text(scene_state);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -124,33 +168,50 @@ void totp_scene_add_new_token_render(Canvas* const canvas, const PluginState* pl
|
|
|
27 - scene_state->screen_y_offset,
|
|
27 - scene_state->screen_y_offset,
|
|
|
scene_state->token_secret,
|
|
scene_state->token_secret,
|
|
|
scene_state->selected_control == TokenSecretTextBox);
|
|
scene_state->selected_control == TokenSecretTextBox);
|
|
|
|
|
+
|
|
|
ui_control_select_render(
|
|
ui_control_select_render(
|
|
|
canvas,
|
|
canvas,
|
|
|
0,
|
|
0,
|
|
|
44 - scene_state->screen_y_offset,
|
|
44 - scene_state->screen_y_offset,
|
|
|
SCREEN_WIDTH,
|
|
SCREEN_WIDTH,
|
|
|
- TOKEN_ALGO_LIST[scene_state->algo],
|
|
|
|
|
- scene_state->selected_control == TokenAlgoSelect);
|
|
|
|
|
|
|
+ TOKEN_TYPE_LIST[scene_state->type],
|
|
|
|
|
+ scene_state->selected_control == TokenTypeSelect);
|
|
|
|
|
+
|
|
|
ui_control_select_render(
|
|
ui_control_select_render(
|
|
|
canvas,
|
|
canvas,
|
|
|
0,
|
|
0,
|
|
|
61 - scene_state->screen_y_offset,
|
|
61 - scene_state->screen_y_offset,
|
|
|
SCREEN_WIDTH,
|
|
SCREEN_WIDTH,
|
|
|
- TOKEN_DIGITS_TEXT_LIST[scene_state->digits_count_index],
|
|
|
|
|
- scene_state->selected_control == TokenLengthSelect);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ TOKEN_ALGO_LIST[scene_state->algo],
|
|
|
|
|
+ scene_state->selected_control == TokenAlgoSelect);
|
|
|
ui_control_select_render(
|
|
ui_control_select_render(
|
|
|
canvas,
|
|
canvas,
|
|
|
0,
|
|
0,
|
|
|
78 - scene_state->screen_y_offset,
|
|
78 - scene_state->screen_y_offset,
|
|
|
SCREEN_WIDTH,
|
|
SCREEN_WIDTH,
|
|
|
- furi_string_get_cstr(scene_state->duration_text),
|
|
|
|
|
- scene_state->selected_control == TokenDurationSelect);
|
|
|
|
|
|
|
+ TOKEN_DIGITS_TEXT_LIST[scene_state->digits_count_index],
|
|
|
|
|
+ scene_state->selected_control == TokenLengthSelect);
|
|
|
|
|
+
|
|
|
|
|
+ if(scene_state->type == TokenTypeTOTP) {
|
|
|
|
|
+ ui_control_select_render(
|
|
|
|
|
+ canvas,
|
|
|
|
|
+ 0,
|
|
|
|
|
+ 95 - scene_state->screen_y_offset,
|
|
|
|
|
+ SCREEN_WIDTH,
|
|
|
|
|
+ furi_string_get_cstr(scene_state->duration_text),
|
|
|
|
|
+ scene_state->selected_control == TokenDurationOrCounterSelect);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ui_control_text_box_render(
|
|
|
|
|
+ canvas,
|
|
|
|
|
+ 95 - scene_state->screen_y_offset,
|
|
|
|
|
+ scene_state->initial_counter,
|
|
|
|
|
+ scene_state->selected_control == TokenDurationOrCounterSelect);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
ui_control_button_render(
|
|
ui_control_button_render(
|
|
|
canvas,
|
|
canvas,
|
|
|
SCREEN_WIDTH_CENTER - 24,
|
|
SCREEN_WIDTH_CENTER - 24,
|
|
|
- 101 - scene_state->screen_y_offset,
|
|
|
|
|
|
|
+ 119 - scene_state->screen_y_offset,
|
|
|
48,
|
|
48,
|
|
|
13,
|
|
13,
|
|
|
"Confirm",
|
|
"Confirm",
|
|
@@ -164,18 +225,6 @@ void totp_scene_add_new_token_render(Canvas* const canvas, const PluginState* pl
|
|
|
canvas_set_font(canvas, FontSecondary);
|
|
canvas_set_font(canvas, FontSecondary);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void update_screen_y_offset(SceneState* scene_state) {
|
|
|
|
|
- if(scene_state->selected_control > TokenLengthSelect) {
|
|
|
|
|
- scene_state->screen_y_offset = 51;
|
|
|
|
|
- } else if(scene_state->selected_control > TokenAlgoSelect) {
|
|
|
|
|
- scene_state->screen_y_offset = 34;
|
|
|
|
|
- } else if(scene_state->selected_control > TokenSecretTextBox) {
|
|
|
|
|
- scene_state->screen_y_offset = 17;
|
|
|
|
|
- } else {
|
|
|
|
|
- scene_state->screen_y_offset = 0;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
bool totp_scene_add_new_token_handle_event(
|
|
bool totp_scene_add_new_token_handle_event(
|
|
|
const PluginEvent* const event,
|
|
const PluginEvent* const event,
|
|
|
PluginState* plugin_state) {
|
|
PluginState* plugin_state) {
|
|
@@ -216,10 +265,14 @@ bool totp_scene_add_new_token_handle_event(
|
|
|
} else if(scene_state->selected_control == TokenLengthSelect) {
|
|
} else if(scene_state->selected_control == TokenLengthSelect) {
|
|
|
totp_roll_value_uint8_t(
|
|
totp_roll_value_uint8_t(
|
|
|
&scene_state->digits_count_index, 1, 0, 2, RollOverflowBehaviorRoll);
|
|
&scene_state->digits_count_index, 1, 0, 2, RollOverflowBehaviorRoll);
|
|
|
- } else if(scene_state->selected_control == TokenDurationSelect) {
|
|
|
|
|
|
|
+ } else if(
|
|
|
|
|
+ scene_state->selected_control == TokenDurationOrCounterSelect &&
|
|
|
|
|
+ scene_state->type == TokenTypeTOTP) {
|
|
|
totp_roll_value_uint8_t(
|
|
totp_roll_value_uint8_t(
|
|
|
&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
|
|
&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
|
|
|
update_duration_text(scene_state);
|
|
update_duration_text(scene_state);
|
|
|
|
|
+ } else if(scene_state->selected_control == TokenTypeSelect) {
|
|
|
|
|
+ totp_roll_value_uint8_t(&scene_state->type, 1, 0, 1, RollOverflowBehaviorRoll);
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
case InputKeyLeft:
|
|
case InputKeyLeft:
|
|
@@ -233,10 +286,14 @@ bool totp_scene_add_new_token_handle_event(
|
|
|
} else if(scene_state->selected_control == TokenLengthSelect) {
|
|
} else if(scene_state->selected_control == TokenLengthSelect) {
|
|
|
totp_roll_value_uint8_t(
|
|
totp_roll_value_uint8_t(
|
|
|
&scene_state->digits_count_index, -1, 0, 2, RollOverflowBehaviorRoll);
|
|
&scene_state->digits_count_index, -1, 0, 2, RollOverflowBehaviorRoll);
|
|
|
- } else if(scene_state->selected_control == TokenDurationSelect) {
|
|
|
|
|
|
|
+ } else if(
|
|
|
|
|
+ scene_state->selected_control == TokenDurationOrCounterSelect &&
|
|
|
|
|
+ scene_state->type == TokenTypeTOTP) {
|
|
|
totp_roll_value_uint8_t(
|
|
totp_roll_value_uint8_t(
|
|
|
&scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
|
|
&scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
|
|
|
update_duration_text(scene_state);
|
|
update_duration_text(scene_state);
|
|
|
|
|
+ } else if(scene_state->selected_control == TokenTypeSelect) {
|
|
|
|
|
+ totp_roll_value_uint8_t(&scene_state->type, -1, 0, 1, RollOverflowBehaviorRoll);
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
case InputKeyOk:
|
|
case InputKeyOk:
|
|
@@ -247,7 +304,7 @@ bool totp_scene_add_new_token_handle_event(
|
|
|
default:
|
|
default:
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) {
|
|
|
|
|
|
|
+ } else if(event->input.type == InputTypeShort && event->input.key == InputKeyOk) {
|
|
|
switch(scene_state->selected_control) {
|
|
switch(scene_state->selected_control) {
|
|
|
case TokenNameTextBox:
|
|
case TokenNameTextBox:
|
|
|
ask_user_input(
|
|
ask_user_input(
|
|
@@ -267,7 +324,14 @@ bool totp_scene_add_new_token_handle_event(
|
|
|
break;
|
|
break;
|
|
|
case TokenLengthSelect:
|
|
case TokenLengthSelect:
|
|
|
break;
|
|
break;
|
|
|
- case TokenDurationSelect:
|
|
|
|
|
|
|
+ case TokenDurationOrCounterSelect:
|
|
|
|
|
+ if(scene_state->type == TokenTypeHOTP) {
|
|
|
|
|
+ ask_user_input(
|
|
|
|
|
+ plugin_state,
|
|
|
|
|
+ "Initial counter",
|
|
|
|
|
+ &scene_state->initial_counter,
|
|
|
|
|
+ &scene_state->initial_counter_length);
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
case ConfirmButton: {
|
|
case ConfirmButton: {
|
|
|
struct TotpAddContext add_context = {
|
|
struct TotpAddContext add_context = {
|
|
@@ -280,19 +344,11 @@ bool totp_scene_add_new_token_handle_event(
|
|
|
if(add_result == TotpIteratorUpdateTokenResultSuccess) {
|
|
if(add_result == TotpIteratorUpdateTokenResultSuccess) {
|
|
|
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
|
|
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken);
|
|
|
} else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
|
|
} else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) {
|
|
|
- DialogMessage* message = dialog_message_alloc();
|
|
|
|
|
- dialog_message_set_buttons(message, "Back", NULL, NULL);
|
|
|
|
|
- dialog_message_set_text(
|
|
|
|
|
- message,
|
|
|
|
|
- "Token secret is invalid",
|
|
|
|
|
- SCREEN_WIDTH_CENTER,
|
|
|
|
|
- SCREEN_HEIGHT_CENTER,
|
|
|
|
|
- AlignCenter,
|
|
|
|
|
- AlignCenter);
|
|
|
|
|
- dialog_message_show(plugin_state->dialogs_app, message);
|
|
|
|
|
- dialog_message_free(message);
|
|
|
|
|
- scene_state->selected_control = TokenSecretTextBox;
|
|
|
|
|
- update_screen_y_offset(scene_state);
|
|
|
|
|
|
|
+ show_invalid_field_message(
|
|
|
|
|
+ plugin_state, TokenSecretTextBox, "Token secret is invalid");
|
|
|
|
|
+ } else if(add_result == TotpIteratorUpdateTokenResultInvalidCounter) {
|
|
|
|
|
+ show_invalid_field_message(
|
|
|
|
|
+ plugin_state, TokenDurationOrCounterSelect, "Initial counter is invalid");
|
|
|
} else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
|
|
} else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) {
|
|
|
totp_dialogs_config_updating_error(plugin_state);
|
|
totp_dialogs_config_updating_error(plugin_state);
|
|
|
}
|
|
}
|