totp_scene_add_new_token.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #include "totp_scene_add_new_token.h"
  2. #include "../../types/common.h"
  3. #include "../../services/ui/constants.h"
  4. #include "../scene_director.h"
  5. #include "totp_input_text.h"
  6. #include "../../types/token_info.h"
  7. #include "../../services/list/list.h"
  8. #include "../../services/base32/base32.h"
  9. #include "../../services/config/config.h"
  10. #include "../../services/ui/ui_controls.h"
  11. #include "../generate_token/totp_scene_generate_token.h"
  12. #define TOKEN_ALGO_LIST_LENGTH 3
  13. char* TOKEN_ALGO_LIST[] = { "SHA1", "SHA256", "SHA512" };
  14. #define TOKEN_DIGITS_LIST_LENGTH 2
  15. char* TOKEN_DIGITS_LIST[] = { "6 digits", "8 digits" };
  16. typedef enum {
  17. TokenNameTextBox,
  18. TokenSecretTextBox,
  19. TokenAlgoSelect,
  20. TokenLengthSelect,
  21. ConfirmButton,
  22. } Control;
  23. typedef struct {
  24. char* token_name;
  25. uint8_t token_name_length;
  26. char* token_secret;
  27. uint8_t token_secret_length;
  28. bool saved;
  29. Control selected_control;
  30. InputTextSceneContext* token_name_input_context;
  31. InputTextSceneContext* token_secret_input_context;
  32. InputTextSceneState* input_state;
  33. uint32_t input_started_at;
  34. int current_token_index;
  35. int32_t screen_y_offset;
  36. TokenHashAlgo algo;
  37. TokenDigitsCount digits_count;
  38. } SceneState;
  39. void totp_scene_add_new_token_init(PluginState* plugin_state) {
  40. UNUSED(plugin_state);
  41. }
  42. static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) {
  43. SceneState* scene_state = result->callback_data;
  44. free(scene_state->token_name);
  45. scene_state->token_name = result->user_input;
  46. scene_state->token_name_length = result->user_input_length;
  47. scene_state->input_started_at = 0;
  48. free(result);
  49. }
  50. static void on_token_secret_user_comitted(InputTextSceneCallbackResult* result) {
  51. SceneState* scene_state = result->callback_data;
  52. free(scene_state->token_secret);
  53. scene_state->token_secret = result->user_input;
  54. scene_state->token_secret_length = result->user_input_length;
  55. scene_state->input_started_at = 0;
  56. free(result);
  57. }
  58. void totp_scene_add_new_token_activate(PluginState* plugin_state, const TokenAddEditSceneContext* context) {
  59. SceneState* scene_state = malloc(sizeof(SceneState));
  60. plugin_state->current_scene_state = scene_state;
  61. scene_state->token_name = "Name";
  62. scene_state->token_name_length = strlen(scene_state->token_name);
  63. scene_state->token_secret = "Secret";
  64. scene_state->token_secret_length = strlen(scene_state->token_secret);
  65. scene_state->token_name_input_context = malloc(sizeof(InputTextSceneContext));
  66. scene_state->token_name_input_context->header_text = "Enter token name";
  67. scene_state->token_name_input_context->callback_data = scene_state;
  68. scene_state->token_name_input_context->callback = on_token_name_user_comitted;
  69. scene_state->token_secret_input_context = malloc(sizeof(InputTextSceneContext));
  70. scene_state->token_secret_input_context->header_text = "Enter token secret";
  71. scene_state->token_secret_input_context->callback_data = scene_state;
  72. scene_state->token_secret_input_context->callback = on_token_secret_user_comitted;
  73. scene_state->screen_y_offset = 0;
  74. scene_state->input_state = NULL;
  75. if (context == NULL) {
  76. scene_state->current_token_index = -1;
  77. } else {
  78. scene_state->current_token_index = context->current_token_index;
  79. }
  80. }
  81. void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) {
  82. SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
  83. if (scene_state->input_started_at > 0) {
  84. totp_input_text_render(canvas, scene_state->input_state);
  85. return;
  86. }
  87. ui_control_text_box_render(canvas, 10 - scene_state->screen_y_offset, scene_state->token_name, scene_state->selected_control == TokenNameTextBox);
  88. ui_control_text_box_render(canvas, 27 - scene_state->screen_y_offset, scene_state->token_secret, scene_state->selected_control == TokenSecretTextBox);
  89. ui_control_select_render(canvas, 44 - scene_state->screen_y_offset, TOKEN_ALGO_LIST[scene_state->algo], scene_state->selected_control == TokenAlgoSelect);
  90. ui_control_select_render(canvas, 63 - scene_state->screen_y_offset, TOKEN_DIGITS_LIST[scene_state->digits_count], scene_state->selected_control == TokenLengthSelect);
  91. ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 24, 85 - scene_state->screen_y_offset, 48, 13, "Confirm", scene_state->selected_control == ConfirmButton);
  92. canvas_set_color(canvas, ColorWhite);
  93. canvas_draw_box(canvas, 0, 0, SCREEN_WIDTH, 10);
  94. canvas_set_color(canvas, ColorBlack);
  95. canvas_set_font(canvas, FontPrimary);
  96. canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Add new token");
  97. canvas_set_font(canvas, FontSecondary);
  98. }
  99. void update_screen_y_offset(SceneState* scene_state) {
  100. if (scene_state->selected_control > TokenAlgoSelect) {
  101. scene_state->screen_y_offset = 35;
  102. } else {
  103. scene_state->screen_y_offset = 0;
  104. }
  105. }
  106. bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state) {
  107. if(event->type == EventTypeKey) {
  108. SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
  109. if (scene_state->input_started_at > 0 && furi_get_tick() - scene_state->input_started_at > 300) {
  110. return totp_input_text_handle_event(event, scene_state->input_state);
  111. }
  112. if (event->input.type == InputTypeLong && event->input.key == InputKeyBack) {
  113. return false;
  114. } else if(event->input.type == InputTypePress) {
  115. switch(event->input.key) {
  116. case InputKeyUp:
  117. if (scene_state->selected_control > TokenNameTextBox) {
  118. scene_state->selected_control--;
  119. update_screen_y_offset(scene_state);
  120. }
  121. break;
  122. case InputKeyDown:
  123. if (scene_state->selected_control < ConfirmButton) {
  124. scene_state->selected_control++;
  125. update_screen_y_offset(scene_state);
  126. }
  127. break;
  128. case InputKeyRight:
  129. if (scene_state->selected_control == TokenAlgoSelect) {
  130. if (scene_state->algo < SHA512) {
  131. scene_state->algo++;
  132. } else {
  133. scene_state->algo = SHA1;
  134. }
  135. }
  136. else if (scene_state->selected_control == TokenLengthSelect) {
  137. if (scene_state->digits_count < TOTP_8_DIGITS) {
  138. scene_state->digits_count++;
  139. } else {
  140. scene_state->digits_count = TOTP_6_DIGITS;
  141. }
  142. }
  143. break;
  144. case InputKeyLeft:
  145. if (scene_state->selected_control == TokenAlgoSelect) {
  146. if (scene_state->algo > SHA1) {
  147. scene_state->algo--;
  148. } else {
  149. scene_state->algo = SHA512;
  150. }
  151. }
  152. else if (scene_state->selected_control == TokenLengthSelect) {
  153. if (scene_state->digits_count > TOTP_6_DIGITS) {
  154. scene_state->digits_count--;
  155. } else {
  156. scene_state->digits_count = TOTP_8_DIGITS;
  157. }
  158. }
  159. break;
  160. case InputKeyOk:
  161. switch (scene_state->selected_control) {
  162. case TokenNameTextBox:
  163. if (scene_state->input_state != NULL) {
  164. totp_input_text_free(scene_state->input_state);
  165. }
  166. scene_state->input_state = totp_input_text_activate(scene_state->token_name_input_context);
  167. scene_state->input_started_at = furi_get_tick();
  168. break;
  169. case TokenSecretTextBox:
  170. if (scene_state->input_state != NULL) {
  171. totp_input_text_free(scene_state->input_state);
  172. }
  173. scene_state->input_state = totp_input_text_activate(scene_state->token_secret_input_context);
  174. scene_state->input_started_at = furi_get_tick();
  175. break;
  176. case TokenAlgoSelect:
  177. break;
  178. case TokenLengthSelect:
  179. break;
  180. case ConfirmButton: {
  181. TokenInfo* tokenInfo = token_info_alloc();
  182. tokenInfo->name = malloc(scene_state->token_name_length + 1);
  183. strcpy(tokenInfo->name, scene_state->token_name);
  184. token_info_set_secret(tokenInfo, scene_state->token_secret, scene_state->token_secret_length, &plugin_state->iv[0]);
  185. tokenInfo->algo = scene_state->algo;
  186. tokenInfo->digits = scene_state->digits_count;
  187. if (plugin_state->tokens_list == NULL) {
  188. plugin_state->tokens_list = list_init_head(tokenInfo);
  189. } else {
  190. list_add(plugin_state->tokens_list, tokenInfo);
  191. }
  192. plugin_state->tokens_count++;
  193. Storage* cfg_storage = totp_open_storage();
  194. FlipperFormat* cfg_file = totp_open_config_file(cfg_storage);
  195. flipper_format_seek_to_end(cfg_file);
  196. totp_config_file_save_new_token(cfg_file, tokenInfo);
  197. totp_close_config_file(cfg_file);
  198. totp_close_storage();
  199. GenerateTokenSceneContext generate_scene_context = { .current_token_index = plugin_state->tokens_count - 1 };
  200. totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
  201. break;
  202. }
  203. }
  204. break;
  205. case InputKeyBack:
  206. if (scene_state->current_token_index >= 0) {
  207. GenerateTokenSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
  208. totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
  209. } else {
  210. totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
  211. }
  212. break;
  213. }
  214. }
  215. }
  216. return true;
  217. }
  218. void totp_scene_add_new_token_deactivate(PluginState* plugin_state) {
  219. if (plugin_state->current_scene_state == NULL) return;
  220. SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
  221. free(scene_state->token_name);
  222. free(scene_state->token_secret);
  223. free(scene_state->token_name_input_context->header_text);
  224. free(scene_state->token_name_input_context);
  225. free(scene_state->token_secret_input_context->header_text);
  226. free(scene_state->token_secret_input_context);
  227. if (scene_state->input_state != NULL) {
  228. totp_input_text_free(scene_state->input_state);
  229. }
  230. free(plugin_state->current_scene_state);
  231. plugin_state->current_scene_state = NULL;
  232. }
  233. void totp_scene_add_new_token_free(PluginState* plugin_state) {
  234. UNUSED(plugin_state);
  235. }