config.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. #include "config.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <linked_list.h>
  5. #include "../../types/common.h"
  6. #include "../../types/token_info.h"
  7. #include "../../features_config.h"
  8. #include "migrations/common_migration.h"
  9. #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator")
  10. #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
  11. #define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup"
  12. #define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp"
  13. #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig"
  14. #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf"
  15. /**
  16. * @brief Opens storage record
  17. * @return Storage record
  18. */
  19. static Storage* totp_open_storage() {
  20. return furi_record_open(RECORD_STORAGE);
  21. }
  22. /**
  23. * @brief Closes storage record
  24. */
  25. static void totp_close_storage() {
  26. furi_record_close(RECORD_STORAGE);
  27. }
  28. /**
  29. * @brief Closes config file
  30. * @param file config file reference
  31. */
  32. static void totp_close_config_file(FlipperFormat* file) {
  33. if(file == NULL) return;
  34. flipper_format_file_close(file);
  35. flipper_format_free(file);
  36. }
  37. /**
  38. * @brief Opens or creates TOTP application standard config file
  39. * @param storage storage record to use
  40. * @param[out] file opened config file
  41. * @return Config file open result
  42. */
  43. static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperFormat** file) {
  44. FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
  45. if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) {
  46. FURI_LOG_D(LOGGING_TAG, "Config file %s found", CONFIG_FILE_PATH);
  47. if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) {
  48. FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH);
  49. totp_close_config_file(fff_data_file);
  50. return TotpConfigFileOpenError;
  51. }
  52. } else if(storage_common_stat(storage, CONFIG_FILE_PATH_PREVIOUS, NULL) == FSE_OK) {
  53. FURI_LOG_D(LOGGING_TAG, "Old config file %s found", CONFIG_FILE_PATH_PREVIOUS);
  54. if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
  55. FURI_LOG_D(
  56. LOGGING_TAG,
  57. "Directory %s doesn't exist. Will create new.",
  58. CONFIG_FILE_DIRECTORY_PATH);
  59. if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
  60. FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
  61. totp_close_config_file(fff_data_file);
  62. return TotpConfigFileOpenError;
  63. }
  64. }
  65. if(storage_common_rename(storage, CONFIG_FILE_PATH_PREVIOUS, CONFIG_FILE_PATH) != FSE_OK) {
  66. FURI_LOG_E(LOGGING_TAG, "Error moving config to %s", CONFIG_FILE_PATH);
  67. totp_close_config_file(fff_data_file);
  68. return TotpConfigFileOpenError;
  69. }
  70. FURI_LOG_I(LOGGING_TAG, "Applied config file path migration");
  71. return totp_open_config_file(storage, file);
  72. } else {
  73. FURI_LOG_D(LOGGING_TAG, "Config file %s is not found. Will create new.", CONFIG_FILE_PATH);
  74. if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
  75. FURI_LOG_D(
  76. LOGGING_TAG,
  77. "Directory %s doesn't exist. Will create new.",
  78. CONFIG_FILE_DIRECTORY_PATH);
  79. if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
  80. FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
  81. return TotpConfigFileOpenError;
  82. }
  83. }
  84. if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) {
  85. totp_close_config_file(fff_data_file);
  86. FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH);
  87. return TotpConfigFileOpenError;
  88. }
  89. flipper_format_write_header_cstr(
  90. fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION);
  91. float tmp_tz = 0;
  92. flipper_format_write_comment_cstr(fff_data_file, " ");
  93. flipper_format_write_comment_cstr(
  94. fff_data_file,
  95. "Timezone offset in hours. Important note: do not put '+' sign for positive values");
  96. flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1);
  97. uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
  98. flipper_format_write_comment_cstr(fff_data_file, " ");
  99. flipper_format_write_comment_cstr(
  100. fff_data_file,
  101. "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)");
  102. flipper_format_write_uint32(
  103. fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
  104. tmp_uint32 = AutomationMethodBadUsb;
  105. flipper_format_write_comment_cstr(fff_data_file, " ");
  106. flipper_format_write_comment_cstr(
  107. fff_data_file,
  108. "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)");
  109. flipper_format_write_uint32(
  110. fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1);
  111. FuriString* temp_str = furi_string_alloc();
  112. flipper_format_write_comment_cstr(fff_data_file, " ");
  113. flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE BEGIN ===");
  114. flipper_format_write_comment_cstr(fff_data_file, " ");
  115. flipper_format_write_comment_cstr(
  116. fff_data_file, "# Token name which will be visible in the UI.");
  117. furi_string_printf(temp_str, "%s: Sample token name", TOTP_CONFIG_KEY_TOKEN_NAME);
  118. flipper_format_write_comment(fff_data_file, temp_str);
  119. flipper_format_write_comment_cstr(fff_data_file, " ");
  120. flipper_format_write_comment_cstr(
  121. fff_data_file,
  122. "# Plain token secret without spaces, dashes and etc, just pure alpha-numeric characters. Important note: plain token will be encrypted and replaced by TOTP app");
  123. furi_string_printf(temp_str, "%s: plaintokensecret", TOTP_CONFIG_KEY_TOKEN_SECRET);
  124. flipper_format_write_comment(fff_data_file, temp_str);
  125. flipper_format_write_comment_cstr(fff_data_file, " ");
  126. furi_string_printf(
  127. temp_str,
  128. " # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s",
  129. TOTP_TOKEN_ALGO_SHA1_NAME,
  130. TOTP_TOKEN_ALGO_SHA256_NAME,
  131. TOTP_TOKEN_ALGO_SHA512_NAME,
  132. TOTP_TOKEN_ALGO_STEAM_NAME,
  133. TOTP_TOKEN_ALGO_SHA1_NAME);
  134. flipper_format_write_comment(fff_data_file, temp_str);
  135. furi_string_printf(
  136. temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME);
  137. flipper_format_write_comment(fff_data_file, temp_str);
  138. flipper_format_write_comment_cstr(fff_data_file, " ");
  139. flipper_format_write_comment_cstr(
  140. fff_data_file,
  141. "# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6");
  142. furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS);
  143. flipper_format_write_comment(fff_data_file, temp_str);
  144. flipper_format_write_comment_cstr(fff_data_file, " ");
  145. flipper_format_write_comment_cstr(
  146. fff_data_file,
  147. "# Token lifetime duration in seconds. Should be between 15 and 255. Majority websites requires 30, however some rare websites may require custom lifetime. If you are not sure which one to use - use 30");
  148. furi_string_printf(temp_str, "%s: 30", TOTP_CONFIG_KEY_TOKEN_DURATION);
  149. flipper_format_write_comment(fff_data_file, temp_str);
  150. flipper_format_write_comment_cstr(fff_data_file, " ");
  151. flipper_format_write_comment_cstr(
  152. fff_data_file,
  153. "# Token input automation features (0 - None, 1 - press \"Enter\" key at the end of automation)");
  154. furi_string_printf(temp_str, "%s: 0", TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES);
  155. flipper_format_write_comment(fff_data_file, temp_str);
  156. flipper_format_write_comment_cstr(fff_data_file, " ");
  157. flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ===");
  158. flipper_format_write_comment_cstr(fff_data_file, " ");
  159. furi_string_free(temp_str);
  160. if(!flipper_format_rewind(fff_data_file)) {
  161. totp_close_config_file(fff_data_file);
  162. FURI_LOG_E(LOGGING_TAG, "Rewind error");
  163. return TotpConfigFileOpenError;
  164. }
  165. }
  166. *file = fff_data_file;
  167. return TotpConfigFileOpenSuccess;
  168. }
  169. static TotpConfigFileUpdateResult
  170. totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) {
  171. TotpConfigFileUpdateResult update_result;
  172. do {
  173. if(!flipper_format_seek_to_end(file)) {
  174. update_result = TotpConfigFileUpdateError;
  175. break;
  176. }
  177. if(!flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) {
  178. update_result = TotpConfigFileUpdateError;
  179. break;
  180. }
  181. bool token_is_valid = token_info->token != NULL && token_info->token_length > 0;
  182. if(!token_is_valid &&
  183. !flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!")) {
  184. update_result = TotpConfigFileUpdateError;
  185. break;
  186. }
  187. if(!flipper_format_write_hex(
  188. file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length)) {
  189. update_result = TotpConfigFileUpdateError;
  190. break;
  191. }
  192. if(!token_is_valid && !flipper_format_write_comment_cstr(file, "!!! WARNING END !!!")) {
  193. update_result = TotpConfigFileUpdateError;
  194. break;
  195. }
  196. if(!flipper_format_write_string_cstr(
  197. file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info))) {
  198. update_result = TotpConfigFileUpdateError;
  199. break;
  200. }
  201. uint32_t tmp_uint32 = token_info->digits;
  202. if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) {
  203. update_result = TotpConfigFileUpdateError;
  204. break;
  205. }
  206. tmp_uint32 = token_info->duration;
  207. if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) {
  208. update_result = TotpConfigFileUpdateError;
  209. break;
  210. }
  211. tmp_uint32 = token_info->automation_features;
  212. if(!flipper_format_write_uint32(
  213. file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) {
  214. update_result = TotpConfigFileUpdateError;
  215. break;
  216. }
  217. update_result = TotpConfigFileUpdateSuccess;
  218. } while(false);
  219. return update_result;
  220. }
  221. TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) {
  222. Storage* cfg_storage = totp_open_storage();
  223. FlipperFormat* file;
  224. TotpConfigFileUpdateResult update_result;
  225. if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
  226. do {
  227. if(totp_config_file_save_new_token_i(file, token_info) !=
  228. TotpConfigFileUpdateSuccess) {
  229. update_result = TotpConfigFileUpdateError;
  230. break;
  231. }
  232. update_result = TotpConfigFileUpdateSuccess;
  233. } while(false);
  234. totp_close_config_file(file);
  235. } else {
  236. update_result = TotpConfigFileUpdateError;
  237. }
  238. totp_close_storage();
  239. return update_result;
  240. }
  241. TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset) {
  242. Storage* cfg_storage = totp_open_storage();
  243. FlipperFormat* file;
  244. TotpConfigFileUpdateResult update_result;
  245. if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
  246. do {
  247. if(!flipper_format_insert_or_update_float(
  248. file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1)) {
  249. update_result = TotpConfigFileUpdateError;
  250. break;
  251. }
  252. update_result = TotpConfigFileUpdateSuccess;
  253. } while(false);
  254. totp_close_config_file(file);
  255. } else {
  256. update_result = TotpConfigFileUpdateError;
  257. }
  258. totp_close_storage();
  259. return update_result;
  260. }
  261. TotpConfigFileUpdateResult
  262. totp_config_file_update_notification_method(NotificationMethod new_notification_method) {
  263. Storage* cfg_storage = totp_open_storage();
  264. FlipperFormat* file;
  265. TotpConfigFileUpdateResult update_result;
  266. if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
  267. do {
  268. uint32_t tmp_uint32 = new_notification_method;
  269. if(!flipper_format_insert_or_update_uint32(
  270. file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
  271. update_result = TotpConfigFileUpdateError;
  272. break;
  273. }
  274. update_result = TotpConfigFileUpdateSuccess;
  275. } while(false);
  276. totp_close_config_file(file);
  277. } else {
  278. update_result = TotpConfigFileUpdateError;
  279. }
  280. totp_close_storage();
  281. return update_result;
  282. }
  283. TotpConfigFileUpdateResult
  284. totp_config_file_update_automation_method(AutomationMethod new_automation_method) {
  285. Storage* cfg_storage = totp_open_storage();
  286. FlipperFormat* file;
  287. TotpConfigFileUpdateResult update_result;
  288. if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
  289. do {
  290. uint32_t tmp_uint32 = new_automation_method;
  291. if(!flipper_format_insert_or_update_uint32(
  292. file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
  293. update_result = TotpConfigFileUpdateError;
  294. break;
  295. }
  296. update_result = TotpConfigFileUpdateSuccess;
  297. } while(false);
  298. totp_close_config_file(file);
  299. } else {
  300. update_result = TotpConfigFileUpdateError;
  301. }
  302. totp_close_storage();
  303. return update_result;
  304. }
  305. TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) {
  306. Storage* cfg_storage = totp_open_storage();
  307. FlipperFormat* file;
  308. TotpConfigFileUpdateResult update_result;
  309. if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
  310. do {
  311. if(!flipper_format_insert_or_update_float(
  312. file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
  313. update_result = TotpConfigFileUpdateError;
  314. break;
  315. }
  316. uint32_t tmp_uint32 = plugin_state->notification_method;
  317. if(!flipper_format_insert_or_update_uint32(
  318. file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
  319. update_result = TotpConfigFileUpdateError;
  320. break;
  321. }
  322. tmp_uint32 = plugin_state->automation_method;
  323. if(!flipper_format_insert_or_update_uint32(
  324. file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
  325. update_result = TotpConfigFileUpdateError;
  326. break;
  327. }
  328. update_result = TotpConfigFileUpdateSuccess;
  329. } while(false);
  330. totp_close_config_file(file);
  331. } else {
  332. update_result = TotpConfigFileUpdateError;
  333. }
  334. totp_close_storage();
  335. return update_result;
  336. }
  337. TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state) {
  338. Storage* storage = totp_open_storage();
  339. FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
  340. TotpConfigFileUpdateResult result = TotpConfigFileUpdateSuccess;
  341. do {
  342. if(!flipper_format_file_open_always(fff_data_file, CONFIG_FILE_TEMP_PATH)) {
  343. result = TotpConfigFileUpdateError;
  344. break;
  345. }
  346. if(!flipper_format_write_header_cstr(
  347. fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION)) {
  348. result = TotpConfigFileUpdateError;
  349. break;
  350. }
  351. if(!flipper_format_write_hex(
  352. fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
  353. result = TotpConfigFileUpdateError;
  354. break;
  355. }
  356. if(!flipper_format_write_hex(
  357. fff_data_file,
  358. TOTP_CONFIG_KEY_CRYPTO_VERIFY,
  359. plugin_state->crypto_verify_data,
  360. plugin_state->crypto_verify_data_length)) {
  361. result = TotpConfigFileUpdateError;
  362. break;
  363. }
  364. if(!flipper_format_write_float(
  365. fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
  366. result = TotpConfigFileUpdateError;
  367. break;
  368. }
  369. if(!flipper_format_write_bool(
  370. fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
  371. result = TotpConfigFileUpdateError;
  372. break;
  373. }
  374. uint32_t tmp_uint32 = plugin_state->notification_method;
  375. if(!flipper_format_write_uint32(
  376. fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
  377. result = TotpConfigFileUpdateError;
  378. break;
  379. }
  380. tmp_uint32 = plugin_state->automation_method;
  381. if(!flipper_format_write_uint32(
  382. fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
  383. result = TotpConfigFileUpdateError;
  384. break;
  385. }
  386. bool tokens_written = true;
  387. TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
  388. const TokenInfo* token_info = node->data;
  389. tokens_written = tokens_written &&
  390. totp_config_file_save_new_token_i(fff_data_file, token_info) ==
  391. TotpConfigFileUpdateSuccess;
  392. });
  393. if(!tokens_written) {
  394. result = TotpConfigFileUpdateError;
  395. break;
  396. }
  397. } while(false);
  398. totp_close_config_file(fff_data_file);
  399. if(result == TotpConfigFileUpdateSuccess) {
  400. if(storage_file_exists(storage, CONFIG_FILE_ORIG_PATH)) {
  401. storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH);
  402. }
  403. if(storage_common_rename(storage, CONFIG_FILE_PATH, CONFIG_FILE_ORIG_PATH) != FSE_OK) {
  404. result = TotpConfigFileUpdateError;
  405. } else if(storage_common_rename(storage, CONFIG_FILE_TEMP_PATH, CONFIG_FILE_PATH) != FSE_OK) {
  406. result = TotpConfigFileUpdateError;
  407. } else if(!storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH)) {
  408. result = TotpConfigFileUpdateError;
  409. }
  410. }
  411. totp_close_storage();
  412. return result;
  413. }
  414. TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state) {
  415. Storage* storage = totp_open_storage();
  416. FlipperFormat* fff_data_file;
  417. TotpConfigFileOpenResult result;
  418. if((result = totp_open_config_file(storage, &fff_data_file)) != TotpConfigFileOpenSuccess) {
  419. totp_close_storage();
  420. return result;
  421. }
  422. plugin_state->timezone_offset = 0;
  423. FuriString* temp_str = furi_string_alloc();
  424. do {
  425. uint32_t file_version;
  426. if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) {
  427. FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
  428. result = TotpConfigFileOpenError;
  429. break;
  430. }
  431. if(file_version < CONFIG_FILE_ACTUAL_VERSION) {
  432. FURI_LOG_I(
  433. LOGGING_TAG,
  434. "Obsolete config file version detected. Current version: %" PRIu32
  435. "; Actual version: %" PRId16,
  436. file_version,
  437. CONFIG_FILE_ACTUAL_VERSION);
  438. totp_close_config_file(fff_data_file);
  439. if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) {
  440. storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH);
  441. }
  442. if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) {
  443. FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH);
  444. if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
  445. result = TotpConfigFileOpenError;
  446. break;
  447. }
  448. FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage);
  449. if(!flipper_format_file_open_existing(
  450. fff_backup_data_file, CONFIG_FILE_BACKUP_PATH)) {
  451. flipper_format_file_close(fff_backup_data_file);
  452. flipper_format_free(fff_backup_data_file);
  453. result = TotpConfigFileOpenError;
  454. break;
  455. }
  456. if(totp_config_migrate_to_latest(fff_data_file, fff_backup_data_file)) {
  457. FURI_LOG_I(
  458. LOGGING_TAG,
  459. "Applied migration to version %" PRId16,
  460. CONFIG_FILE_ACTUAL_VERSION);
  461. file_version = CONFIG_FILE_ACTUAL_VERSION;
  462. } else {
  463. FURI_LOG_W(
  464. LOGGING_TAG,
  465. "An error occurred during migration to version %" PRId16,
  466. CONFIG_FILE_ACTUAL_VERSION);
  467. result = TotpConfigFileOpenError;
  468. break;
  469. }
  470. flipper_format_file_close(fff_backup_data_file);
  471. flipper_format_free(fff_backup_data_file);
  472. flipper_format_rewind(fff_data_file);
  473. } else {
  474. FURI_LOG_E(
  475. LOGGING_TAG,
  476. "An error occurred during taking backup of %s into %s before migration",
  477. CONFIG_FILE_PATH,
  478. CONFIG_FILE_BACKUP_PATH);
  479. result = TotpConfigFileOpenError;
  480. break;
  481. }
  482. }
  483. if(!flipper_format_read_hex(
  484. fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
  485. FURI_LOG_D(LOGGING_TAG, "Missing base IV");
  486. }
  487. if(!flipper_format_rewind(fff_data_file)) {
  488. result = TotpConfigFileOpenError;
  489. break;
  490. }
  491. uint32_t crypto_size;
  492. if(flipper_format_get_value_count(
  493. fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) &&
  494. crypto_size > 0) {
  495. plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size);
  496. furi_check(plugin_state->crypto_verify_data != NULL);
  497. plugin_state->crypto_verify_data_length = crypto_size;
  498. if(!flipper_format_read_hex(
  499. fff_data_file,
  500. TOTP_CONFIG_KEY_CRYPTO_VERIFY,
  501. plugin_state->crypto_verify_data,
  502. crypto_size)) {
  503. FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token");
  504. free(plugin_state->crypto_verify_data);
  505. plugin_state->crypto_verify_data = NULL;
  506. plugin_state->crypto_verify_data_length = 0;
  507. }
  508. } else {
  509. plugin_state->crypto_verify_data = NULL;
  510. plugin_state->crypto_verify_data_length = 0;
  511. }
  512. if(!flipper_format_rewind(fff_data_file)) {
  513. result = TotpConfigFileOpenError;
  514. break;
  515. }
  516. if(!flipper_format_read_float(
  517. fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
  518. plugin_state->timezone_offset = 0;
  519. FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0");
  520. }
  521. if(!flipper_format_rewind(fff_data_file)) {
  522. result = TotpConfigFileOpenError;
  523. break;
  524. }
  525. if(!flipper_format_read_bool(
  526. fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
  527. plugin_state->pin_set = true;
  528. }
  529. flipper_format_rewind(fff_data_file);
  530. uint32_t tmp_uint32;
  531. if(!flipper_format_read_uint32(
  532. fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
  533. tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
  534. }
  535. plugin_state->notification_method = tmp_uint32;
  536. flipper_format_rewind(fff_data_file);
  537. if(!flipper_format_read_uint32(
  538. fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
  539. tmp_uint32 = AutomationMethodBadUsb;
  540. }
  541. plugin_state->automation_method = tmp_uint32;
  542. } while(false);
  543. furi_string_free(temp_str);
  544. totp_close_config_file(fff_data_file);
  545. totp_close_storage();
  546. return result;
  547. }
  548. TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) {
  549. Storage* storage = totp_open_storage();
  550. FlipperFormat* fff_data_file;
  551. if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
  552. totp_close_storage();
  553. return TokenLoadingResultError;
  554. }
  555. FuriString* temp_str = furi_string_alloc();
  556. uint32_t temp_data32;
  557. if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
  558. FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
  559. totp_close_storage();
  560. furi_string_free(temp_str);
  561. return TokenLoadingResultError;
  562. }
  563. TokenLoadingResult result = TokenLoadingResultSuccess;
  564. uint16_t index = 0;
  565. bool has_any_plain_secret = false;
  566. while(true) {
  567. if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
  568. break;
  569. }
  570. TokenInfo* tokenInfo = token_info_alloc();
  571. size_t temp_cstr_len = furi_string_size(temp_str);
  572. tokenInfo->name = malloc(temp_cstr_len + 1);
  573. furi_check(tokenInfo->name != NULL);
  574. strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1);
  575. uint32_t secret_bytes_count;
  576. if(!flipper_format_get_value_count(
  577. fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) {
  578. secret_bytes_count = 0;
  579. }
  580. if(secret_bytes_count == 1) { // Plain secret key
  581. if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) {
  582. if(token_info_set_secret(
  583. tokenInfo,
  584. furi_string_get_cstr(temp_str),
  585. furi_string_size(temp_str),
  586. PLAIN_TOKEN_ENCODING_BASE32,
  587. &plugin_state->iv[0])) {
  588. FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
  589. } else {
  590. tokenInfo->token = NULL;
  591. tokenInfo->token_length = 0;
  592. FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name);
  593. result = TokenLoadingResultWarning;
  594. }
  595. } else {
  596. tokenInfo->token = NULL;
  597. tokenInfo->token_length = 0;
  598. result = TokenLoadingResultWarning;
  599. }
  600. has_any_plain_secret = true;
  601. } else { // encrypted
  602. tokenInfo->token_length = secret_bytes_count;
  603. if(secret_bytes_count > 0) {
  604. tokenInfo->token = malloc(tokenInfo->token_length);
  605. furi_check(tokenInfo->token != NULL);
  606. if(!flipper_format_read_hex(
  607. fff_data_file,
  608. TOTP_CONFIG_KEY_TOKEN_SECRET,
  609. tokenInfo->token,
  610. tokenInfo->token_length)) {
  611. free(tokenInfo->token);
  612. tokenInfo->token = NULL;
  613. tokenInfo->token_length = 0;
  614. result = TokenLoadingResultWarning;
  615. }
  616. } else {
  617. tokenInfo->token = NULL;
  618. result = TokenLoadingResultWarning;
  619. }
  620. }
  621. if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str) ||
  622. !token_info_set_algo_from_str(tokenInfo, temp_str)) {
  623. tokenInfo->algo = SHA1;
  624. }
  625. if(!flipper_format_read_uint32(
  626. fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) ||
  627. !token_info_set_digits_from_int(tokenInfo, temp_data32)) {
  628. tokenInfo->digits = TOTP_6_DIGITS;
  629. }
  630. if(!flipper_format_read_uint32(
  631. fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) ||
  632. !token_info_set_duration_from_int(tokenInfo, temp_data32)) {
  633. tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT;
  634. }
  635. if(flipper_format_read_uint32(
  636. fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) {
  637. tokenInfo->automation_features = temp_data32;
  638. } else {
  639. tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE;
  640. }
  641. FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name);
  642. TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check);
  643. index++;
  644. }
  645. plugin_state->tokens_count = index;
  646. plugin_state->token_list_loaded = true;
  647. FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index);
  648. furi_string_free(temp_str);
  649. totp_close_config_file(fff_data_file);
  650. totp_close_storage();
  651. if(has_any_plain_secret) {
  652. totp_full_save_config_file(plugin_state);
  653. }
  654. return result;
  655. }
  656. TotpConfigFileUpdateResult
  657. totp_config_file_update_crypto_signatures(const PluginState* plugin_state) {
  658. Storage* storage = totp_open_storage();
  659. FlipperFormat* config_file;
  660. TotpConfigFileUpdateResult update_result;
  661. if(totp_open_config_file(storage, &config_file) == TotpConfigFileOpenSuccess) {
  662. do {
  663. if(!flipper_format_insert_or_update_hex(
  664. config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) {
  665. update_result = TotpConfigFileUpdateError;
  666. break;
  667. }
  668. if(!flipper_format_insert_or_update_hex(
  669. config_file,
  670. TOTP_CONFIG_KEY_CRYPTO_VERIFY,
  671. plugin_state->crypto_verify_data,
  672. plugin_state->crypto_verify_data_length)) {
  673. update_result = TotpConfigFileUpdateError;
  674. break;
  675. }
  676. if(!flipper_format_insert_or_update_bool(
  677. config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
  678. update_result = TotpConfigFileUpdateError;
  679. break;
  680. }
  681. update_result = TotpConfigFileUpdateSuccess;
  682. } while(false);
  683. totp_close_config_file(config_file);
  684. } else {
  685. update_result = TotpConfigFileUpdateError;
  686. }
  687. totp_close_storage();
  688. return update_result;
  689. }
  690. void totp_config_file_reset() {
  691. Storage* storage = totp_open_storage();
  692. storage_simply_remove(storage, CONFIG_FILE_PATH);
  693. totp_close_storage();
  694. }