seos.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #include "seos_i.h"
  2. #define TAG "Seos"
  3. #define SEOS_KEYS_FILENAME "keys"
  4. static void seos_ble_connection_status_callback(BtStatus status, void* context) {
  5. furi_assert(context);
  6. Seos* seos = context;
  7. FURI_LOG_D(TAG, "seos_ble_connection_status_callback %d", (status == BtStatusConnected));
  8. if(status == BtStatusConnected) {
  9. view_dispatcher_send_custom_event(seos->view_dispatcher, SeosCustomEventConnected);
  10. }
  11. /*
  12. ble_hid->is_connected = (status == BtStatusConnected);
  13. if(ble_hid->state_callback) {
  14. ble_hid->state_callback(ble_hid->is_connected, ble_hid->callback_context);
  15. }
  16. */
  17. }
  18. bool seos_load_keys(Seos* seos) {
  19. const char* file_header = "Seos keys";
  20. const uint32_t file_version = 1;
  21. bool parsed = false;
  22. FlipperFormat* file = flipper_format_file_alloc(seos->storage);
  23. FuriString* path = furi_string_alloc();
  24. FuriString* temp_str = furi_string_alloc();
  25. uint32_t version = 0;
  26. do {
  27. furi_string_printf(
  28. path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, SEOS_KEYS_FILENAME, ".txt");
  29. // Open file
  30. if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
  31. if(!flipper_format_read_header(file, temp_str, &version)) break;
  32. if(!furi_string_equal_str(temp_str, file_header) || (version != file_version)) {
  33. break;
  34. }
  35. if(!flipper_format_read_uint32(file, "SEOS_ADF_OID_LEN", (uint32_t*)&SEOS_ADF_OID_LEN, 1))
  36. break;
  37. if(!flipper_format_read_hex(file, "SEOS_ADF_OID", SEOS_ADF_OID, SEOS_ADF_OID_LEN)) break;
  38. if(!flipper_format_read_hex(file, "SEOS_ADF1_PRIV_ENC", SEOS_ADF1_PRIV_ENC, 16)) break;
  39. if(!flipper_format_read_hex(file, "SEOS_ADF1_PRIV_MAC", SEOS_ADF1_PRIV_MAC, 16)) break;
  40. if(!flipper_format_read_hex(file, "SEOS_ADF1_READ", SEOS_ADF1_READ, 16)) break;
  41. parsed = true;
  42. } while(false);
  43. if(parsed) {
  44. FURI_LOG_I(TAG, "Keys loaded");
  45. BitBuffer* tmp = bit_buffer_alloc(SEOS_ADF_OID_LEN);
  46. bit_buffer_append_bytes(tmp, SEOS_ADF_OID, SEOS_ADF_OID_LEN);
  47. seos_log_bitbuffer(TAG, "Keys for ADF OID loaded", tmp);
  48. bit_buffer_free(tmp);
  49. } else {
  50. FURI_LOG_I(TAG, "Using default keys");
  51. }
  52. furi_string_free(path);
  53. furi_string_free(temp_str);
  54. flipper_format_free(file);
  55. return parsed;
  56. }
  57. bool seos_custom_event_callback(void* context, uint32_t event) {
  58. furi_assert(context);
  59. Seos* seos = context;
  60. return scene_manager_handle_custom_event(seos->scene_manager, event);
  61. }
  62. bool seos_back_event_callback(void* context) {
  63. furi_assert(context);
  64. Seos* seos = context;
  65. return scene_manager_handle_back_event(seos->scene_manager);
  66. }
  67. void seos_tick_event_callback(void* context) {
  68. furi_assert(context);
  69. Seos* seos = context;
  70. scene_manager_handle_tick_event(seos->scene_manager);
  71. }
  72. Seos* seos_alloc() {
  73. Seos* seos = malloc(sizeof(Seos));
  74. seos->has_ble = false;
  75. furi_hal_power_enable_otg();
  76. seos->view_dispatcher = view_dispatcher_alloc();
  77. seos->scene_manager = scene_manager_alloc(&seos_scene_handlers, seos);
  78. view_dispatcher_set_event_callback_context(seos->view_dispatcher, seos);
  79. view_dispatcher_set_custom_event_callback(seos->view_dispatcher, seos_custom_event_callback);
  80. view_dispatcher_set_navigation_event_callback(seos->view_dispatcher, seos_back_event_callback);
  81. view_dispatcher_set_tick_event_callback(seos->view_dispatcher, seos_tick_event_callback, 100);
  82. seos->nfc = nfc_alloc();
  83. // Nfc device
  84. seos->nfc_device = nfc_device_alloc();
  85. nfc_device_set_loading_callback(seos->nfc_device, seos_show_loading_popup, seos);
  86. // Open GUI record
  87. seos->gui = furi_record_open(RECORD_GUI);
  88. view_dispatcher_attach_to_gui(seos->view_dispatcher, seos->gui, ViewDispatcherTypeFullscreen);
  89. // Open Notification record
  90. seos->notifications = furi_record_open(RECORD_NOTIFICATION);
  91. // Submenu
  92. seos->submenu = submenu_alloc();
  93. view_dispatcher_add_view(seos->view_dispatcher, SeosViewMenu, submenu_get_view(seos->submenu));
  94. // Popup
  95. seos->popup = popup_alloc();
  96. view_dispatcher_add_view(seos->view_dispatcher, SeosViewPopup, popup_get_view(seos->popup));
  97. // Loading
  98. seos->loading = loading_alloc();
  99. view_dispatcher_add_view(
  100. seos->view_dispatcher, SeosViewLoading, loading_get_view(seos->loading));
  101. // Text Input
  102. seos->text_input = text_input_alloc();
  103. view_dispatcher_add_view(
  104. seos->view_dispatcher, SeosViewTextInput, text_input_get_view(seos->text_input));
  105. // TextBox
  106. seos->text_box = text_box_alloc();
  107. view_dispatcher_add_view(
  108. seos->view_dispatcher, SeosViewTextBox, text_box_get_view(seos->text_box));
  109. seos->text_box_store = furi_string_alloc();
  110. // Custom Widget
  111. seos->widget = widget_alloc();
  112. view_dispatcher_add_view(seos->view_dispatcher, SeosViewWidget, widget_get_view(seos->widget));
  113. seos->storage = furi_record_open(RECORD_STORAGE);
  114. seos->dialogs = furi_record_open(RECORD_DIALOGS);
  115. seos->load_path = furi_string_alloc();
  116. seos->seos_emulator = seos_emulator_alloc(&seos->credential);
  117. seos->keys_loaded = seos_load_keys(seos);
  118. seos->bt = furi_record_open(RECORD_BT);
  119. bt_disconnect(seos->bt);
  120. // Wait 2nd core to update nvm storage
  121. furi_delay_ms(200);
  122. seos->ble_profile = bt_profile_start(seos->bt, ble_profile_seos, NULL);
  123. furi_check(seos->ble_profile);
  124. bt_set_status_changed_callback(seos->bt, seos_ble_connection_status_callback, seos);
  125. furi_hal_bt_start_advertising();
  126. return seos;
  127. }
  128. void seos_free(Seos* seos) {
  129. furi_assert(seos);
  130. furi_hal_power_disable_otg();
  131. nfc_free(seos->nfc);
  132. // Nfc device
  133. nfc_device_free(seos->nfc_device);
  134. // Submenu
  135. view_dispatcher_remove_view(seos->view_dispatcher, SeosViewMenu);
  136. submenu_free(seos->submenu);
  137. // Popup
  138. view_dispatcher_remove_view(seos->view_dispatcher, SeosViewPopup);
  139. popup_free(seos->popup);
  140. // Loading
  141. view_dispatcher_remove_view(seos->view_dispatcher, SeosViewLoading);
  142. loading_free(seos->loading);
  143. // TextInput
  144. view_dispatcher_remove_view(seos->view_dispatcher, SeosViewTextInput);
  145. text_input_free(seos->text_input);
  146. // TextBox
  147. view_dispatcher_remove_view(seos->view_dispatcher, SeosViewTextBox);
  148. text_box_free(seos->text_box);
  149. furi_string_free(seos->text_box_store);
  150. // Custom Widget
  151. view_dispatcher_remove_view(seos->view_dispatcher, SeosViewWidget);
  152. widget_free(seos->widget);
  153. // View Dispatcher
  154. view_dispatcher_free(seos->view_dispatcher);
  155. // Scene Manager
  156. scene_manager_free(seos->scene_manager);
  157. // GUI
  158. furi_record_close(RECORD_GUI);
  159. seos->gui = NULL;
  160. // Notifications
  161. furi_record_close(RECORD_NOTIFICATION);
  162. seos->notifications = NULL;
  163. furi_string_free(seos->load_path);
  164. furi_record_close(RECORD_STORAGE);
  165. furi_record_close(RECORD_DIALOGS);
  166. if(seos->seos_emulator) {
  167. seos_emulator_free(seos->seos_emulator);
  168. seos->seos_emulator = NULL;
  169. }
  170. furi_hal_bt_stop_advertising();
  171. bt_set_status_changed_callback(seos->bt, NULL, NULL);
  172. bt_disconnect(seos->bt);
  173. // Wait 2nd core to update nvm storage
  174. furi_delay_ms(200);
  175. bt_keys_storage_set_default_path(seos->bt);
  176. furi_check(bt_profile_restore_default(seos->bt));
  177. furi_record_close(RECORD_BT);
  178. free(seos);
  179. }
  180. void seos_text_store_set(Seos* seos, const char* text, ...) {
  181. va_list args;
  182. va_start(args, text);
  183. vsnprintf(seos->text_store, sizeof(seos->text_store), text, args);
  184. va_end(args);
  185. }
  186. void seos_text_store_clear(Seos* seos) {
  187. memset(seos->text_store, 0, sizeof(seos->text_store));
  188. }
  189. bool seos_credential_save(Seos* seos, const char* dev_name) {
  190. bool saved = false;
  191. FlipperFormat* file = flipper_format_file_alloc(seos->storage);
  192. FuriString* temp_str = furi_string_alloc();
  193. bool use_load_path = true;
  194. do {
  195. if(use_load_path && !furi_string_empty(seos->load_path)) {
  196. // Get directory name
  197. path_extract_dirname(furi_string_get_cstr(seos->load_path), temp_str);
  198. // Make path to file to save
  199. furi_string_cat_printf(temp_str, "/%s%s", dev_name, SEOS_APP_EXTENSION);
  200. } else {
  201. // First remove file if it was saved
  202. furi_string_printf(
  203. temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, dev_name, SEOS_APP_EXTENSION);
  204. }
  205. // Open file
  206. if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
  207. // Write header
  208. if(!flipper_format_write_header_cstr(file, seos_file_header, seos_file_version)) break;
  209. if(!flipper_format_write_uint32(
  210. file, "Diversifier Length", (uint32_t*)&(seos->credential.diversifier_len), 1))
  211. break;
  212. if(!flipper_format_write_hex(
  213. file, "Diversifier", seos->credential.diversifier, seos->credential.diversifier_len))
  214. break;
  215. if(!flipper_format_write_uint32(
  216. file, "SIO Length", (uint32_t*)&(seos->credential.sio_len), 1))
  217. break;
  218. if(!flipper_format_write_hex(file, "SIO", seos->credential.sio, seos->credential.sio_len))
  219. break;
  220. saved = true;
  221. } while(false);
  222. if(!saved) {
  223. dialog_message_show_storage_error(seos->dialogs, "Can not save\nfile");
  224. }
  225. furi_string_free(temp_str);
  226. flipper_format_free(file);
  227. return saved;
  228. }
  229. static const NotificationSequence seos_sequence_blink_start_blue = {
  230. &message_blink_start_10,
  231. &message_blink_set_color_blue,
  232. &message_do_not_reset,
  233. NULL,
  234. };
  235. static const NotificationSequence seos_sequence_blink_stop = {
  236. &message_blink_stop,
  237. NULL,
  238. };
  239. void seos_blink_start(Seos* seos) {
  240. notification_message(seos->notifications, &seos_sequence_blink_start_blue);
  241. }
  242. void seos_blink_stop(Seos* seos) {
  243. notification_message(seos->notifications, &seos_sequence_blink_stop);
  244. }
  245. void seos_show_loading_popup(void* context, bool show) {
  246. Seos* seos = context;
  247. if(show) {
  248. // Raise timer priority so that animations can play
  249. furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated);
  250. view_dispatcher_switch_to_view(seos->view_dispatcher, SeosViewLoading);
  251. } else {
  252. // Restore default timer priority
  253. furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal);
  254. }
  255. }
  256. int32_t seos_app(void* p) {
  257. UNUSED(p);
  258. Seos* seos = seos_alloc();
  259. if(seos->keys_loaded) {
  260. scene_manager_next_scene(seos->scene_manager, SeosSceneMainMenu);
  261. } else {
  262. scene_manager_next_scene(seos->scene_manager, SeosSceneZeroKeys);
  263. }
  264. view_dispatcher_run(seos->view_dispatcher);
  265. seos_free(seos);
  266. return 0;
  267. }