totp_scene_add_new_token.c 12 KB

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