totp_scene_add_new_token.c 12 KB

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