totp_scene_add_new_token.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. size_t token_name_length;
  26. char* token_secret;
  27. size_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. int16_t current_token_index;
  35. int16_t screen_y_offset;
  36. TokenHashAlgo algo;
  37. TokenDigitsCount digits_count;
  38. } SceneState;
  39. void totp_scene_add_new_token_init(const 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(
  59. PluginState* plugin_state,
  60. const TokenAddEditSceneContext* context) {
  61. SceneState* scene_state = malloc(sizeof(SceneState));
  62. plugin_state->current_scene_state = scene_state;
  63. scene_state->token_name = "Name";
  64. scene_state->token_name_length = strlen(scene_state->token_name);
  65. scene_state->token_secret = "Secret";
  66. scene_state->token_secret_length = strlen(scene_state->token_secret);
  67. scene_state->token_name_input_context = malloc(sizeof(InputTextSceneContext));
  68. scene_state->token_name_input_context->header_text = "Enter token name";
  69. scene_state->token_name_input_context->callback_data = scene_state;
  70. scene_state->token_name_input_context->callback = on_token_name_user_comitted;
  71. scene_state->token_secret_input_context = malloc(sizeof(InputTextSceneContext));
  72. scene_state->token_secret_input_context->header_text = "Enter token secret";
  73. scene_state->token_secret_input_context->callback_data = scene_state;
  74. scene_state->token_secret_input_context->callback = on_token_secret_user_comitted;
  75. scene_state->screen_y_offset = 0;
  76. scene_state->input_state = NULL;
  77. if(context == NULL) {
  78. scene_state->current_token_index = -1;
  79. } else {
  80. scene_state->current_token_index = context->current_token_index;
  81. }
  82. }
  83. void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) {
  84. SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
  85. if(scene_state->input_started_at > 0) {
  86. totp_input_text_render(canvas, scene_state->input_state);
  87. return;
  88. }
  89. ui_control_text_box_render(
  90. canvas,
  91. 10 - scene_state->screen_y_offset,
  92. scene_state->token_name,
  93. scene_state->selected_control == TokenNameTextBox);
  94. ui_control_text_box_render(
  95. canvas,
  96. 27 - scene_state->screen_y_offset,
  97. scene_state->token_secret,
  98. scene_state->selected_control == TokenSecretTextBox);
  99. ui_control_select_render(
  100. canvas,
  101. 0,
  102. 44 - scene_state->screen_y_offset,
  103. SCREEN_WIDTH,
  104. TOKEN_ALGO_LIST[scene_state->algo],
  105. scene_state->selected_control == TokenAlgoSelect);
  106. ui_control_select_render(
  107. canvas,
  108. 0,
  109. 63 - scene_state->screen_y_offset,
  110. SCREEN_WIDTH,
  111. TOKEN_DIGITS_LIST[scene_state->digits_count],
  112. scene_state->selected_control == TokenLengthSelect);
  113. ui_control_button_render(
  114. canvas,
  115. SCREEN_WIDTH_CENTER - 24,
  116. 85 - scene_state->screen_y_offset,
  117. 48,
  118. 13,
  119. "Confirm",
  120. scene_state->selected_control == ConfirmButton);
  121. canvas_set_color(canvas, ColorWhite);
  122. canvas_draw_box(canvas, 0, 0, SCREEN_WIDTH, 10);
  123. canvas_set_color(canvas, ColorBlack);
  124. canvas_set_font(canvas, FontPrimary);
  125. canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Add new token");
  126. canvas_set_font(canvas, FontSecondary);
  127. }
  128. void update_screen_y_offset(SceneState* scene_state) {
  129. if(scene_state->selected_control > TokenAlgoSelect) {
  130. scene_state->screen_y_offset = 35;
  131. } else {
  132. scene_state->screen_y_offset = 0;
  133. }
  134. }
  135. bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state) {
  136. if(event->type == EventTypeKey) {
  137. SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
  138. if(scene_state->input_started_at > 0 &&
  139. furi_get_tick() - scene_state->input_started_at > 300) {
  140. return totp_input_text_handle_event(event, scene_state->input_state);
  141. }
  142. if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) {
  143. return false;
  144. } else if(event->input.type == InputTypePress) {
  145. switch(event->input.key) {
  146. case InputKeyUp:
  147. if(scene_state->selected_control > TokenNameTextBox) {
  148. scene_state->selected_control--;
  149. update_screen_y_offset(scene_state);
  150. }
  151. break;
  152. case InputKeyDown:
  153. if(scene_state->selected_control < ConfirmButton) {
  154. scene_state->selected_control++;
  155. update_screen_y_offset(scene_state);
  156. }
  157. break;
  158. case InputKeyRight:
  159. if(scene_state->selected_control == TokenAlgoSelect) {
  160. if(scene_state->algo < SHA512) {
  161. scene_state->algo++;
  162. } else {
  163. scene_state->algo = SHA1;
  164. }
  165. } else if(scene_state->selected_control == TokenLengthSelect) {
  166. if(scene_state->digits_count < TOTP_8_DIGITS) {
  167. scene_state->digits_count++;
  168. } else {
  169. scene_state->digits_count = TOTP_6_DIGITS;
  170. }
  171. }
  172. break;
  173. case InputKeyLeft:
  174. if(scene_state->selected_control == TokenAlgoSelect) {
  175. if(scene_state->algo > SHA1) {
  176. scene_state->algo--;
  177. } else {
  178. scene_state->algo = SHA512;
  179. }
  180. } else if(scene_state->selected_control == TokenLengthSelect) {
  181. if(scene_state->digits_count > TOTP_6_DIGITS) {
  182. scene_state->digits_count--;
  183. } else {
  184. scene_state->digits_count = TOTP_8_DIGITS;
  185. }
  186. }
  187. break;
  188. case InputKeyOk:
  189. switch(scene_state->selected_control) {
  190. case TokenNameTextBox:
  191. if(scene_state->input_state != NULL) {
  192. totp_input_text_free(scene_state->input_state);
  193. }
  194. scene_state->input_state =
  195. totp_input_text_activate(scene_state->token_name_input_context);
  196. scene_state->input_started_at = furi_get_tick();
  197. break;
  198. case TokenSecretTextBox:
  199. if(scene_state->input_state != NULL) {
  200. totp_input_text_free(scene_state->input_state);
  201. }
  202. scene_state->input_state =
  203. totp_input_text_activate(scene_state->token_secret_input_context);
  204. scene_state->input_started_at = furi_get_tick();
  205. break;
  206. case TokenAlgoSelect:
  207. break;
  208. case TokenLengthSelect:
  209. break;
  210. case ConfirmButton: {
  211. TokenInfo* tokenInfo = token_info_alloc();
  212. bool token_secret_set = token_info_set_secret(
  213. tokenInfo,
  214. scene_state->token_secret,
  215. scene_state->token_secret_length,
  216. &plugin_state->iv[0]);
  217. if(token_secret_set) {
  218. tokenInfo->name = malloc(scene_state->token_name_length + 1);
  219. strlcpy(
  220. tokenInfo->name,
  221. scene_state->token_name,
  222. scene_state->token_name_length + 1);
  223. tokenInfo->algo = scene_state->algo;
  224. tokenInfo->digits = scene_state->digits_count;
  225. if(plugin_state->tokens_list == NULL) {
  226. plugin_state->tokens_list = list_init_head(tokenInfo);
  227. } else {
  228. list_add(plugin_state->tokens_list, tokenInfo);
  229. }
  230. plugin_state->tokens_count++;
  231. totp_config_file_save_new_token(tokenInfo);
  232. GenerateTokenSceneContext generate_scene_context = {
  233. .current_token_index = plugin_state->tokens_count - 1};
  234. totp_scene_director_activate_scene(
  235. plugin_state, TotpSceneGenerateToken, &generate_scene_context);
  236. } else {
  237. token_info_free(tokenInfo);
  238. DialogMessage* message = dialog_message_alloc();
  239. dialog_message_set_buttons(message, "Back", NULL, NULL);
  240. dialog_message_set_text(
  241. message,
  242. "Token secret is invalid",
  243. SCREEN_WIDTH_CENTER,
  244. SCREEN_HEIGHT_CENTER,
  245. AlignCenter,
  246. AlignCenter);
  247. dialog_message_show(plugin_state->dialogs, message);
  248. dialog_message_free(message);
  249. scene_state->selected_control = TokenSecretTextBox;
  250. update_screen_y_offset(scene_state);
  251. }
  252. break;
  253. }
  254. }
  255. break;
  256. case InputKeyBack:
  257. if(scene_state->current_token_index >= 0) {
  258. GenerateTokenSceneContext generate_scene_context = {
  259. .current_token_index = scene_state->current_token_index};
  260. totp_scene_director_activate_scene(
  261. plugin_state, TotpSceneGenerateToken, &generate_scene_context);
  262. } else {
  263. totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
  264. }
  265. break;
  266. }
  267. }
  268. }
  269. return true;
  270. }
  271. void totp_scene_add_new_token_deactivate(PluginState* plugin_state) {
  272. if(plugin_state->current_scene_state == NULL) return;
  273. SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
  274. free(scene_state->token_name);
  275. free(scene_state->token_secret);
  276. free(scene_state->token_name_input_context->header_text);
  277. free(scene_state->token_name_input_context);
  278. free(scene_state->token_secret_input_context->header_text);
  279. free(scene_state->token_secret_input_context);
  280. if(scene_state->input_state != NULL) {
  281. totp_input_text_free(scene_state->input_state);
  282. }
  283. free(plugin_state->current_scene_state);
  284. plugin_state->current_scene_state = NULL;
  285. }
  286. void totp_scene_add_new_token_free(const PluginState* plugin_state) {
  287. UNUSED(plugin_state);
  288. }