uhf_device.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #include "uhf_device.h"
  2. #include <toolbox/path.h>
  3. #include <flipper_format/flipper_format.h>
  4. #include <uhf_rfid_icons.h>
  5. #define TAG "UHFDevice"
  6. static const char* uhf_file_header = "Flipper UHF RFID device";
  7. static const uint32_t uhf_file_version = 1;
  8. static const uint8_t bank_data_start = 20;
  9. static const uint8_t bank_data_length = 16;
  10. UHFDevice* uhf_device_alloc() {
  11. UHFDevice* uhf_device = malloc(sizeof(UHFDevice));
  12. uhf_device->storage = furi_record_open(RECORD_STORAGE);
  13. uhf_device->dialogs = furi_record_open(RECORD_DIALOGS);
  14. uhf_device->load_path = furi_string_alloc();
  15. return uhf_device;
  16. }
  17. void uhf_device_set_name(UHFDevice* dev, const char* name) {
  18. furi_assert(dev);
  19. strlcpy(dev->dev_name, name, UHF_DEV_NAME_MAX_LEN);
  20. }
  21. static bool uhf_device_save_file(
  22. UHFDevice* dev,
  23. const char* dev_name,
  24. const char* folder,
  25. const char* extension,
  26. bool use_load_path) {
  27. furi_assert(dev);
  28. UHFResponseData* uhf_response_data = dev->dev_data;
  29. bool saved = false;
  30. FlipperFormat* file = flipper_format_file_alloc(dev->storage);
  31. FuriString* temp_str;
  32. temp_str = furi_string_alloc();
  33. do {
  34. if(use_load_path && !furi_string_empty(dev->load_path)) {
  35. // Get directory name
  36. path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str);
  37. // Make path to file to save
  38. furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension);
  39. } else {
  40. // First remove uhf device file if it was saved
  41. furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
  42. }
  43. // Open file
  44. if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
  45. // Write header
  46. if(!flipper_format_write_header_cstr(file, uhf_file_header, uhf_file_version)) break;
  47. // write rfu data to file
  48. UHFData* rfu_data = uhf_response_data_get_uhf_data(uhf_response_data, 1);
  49. if(rfu_data->length) {
  50. if(!flipper_format_write_hex(
  51. file, UHF_RFU_BANK, rfu_data->data + bank_data_start, bank_data_length))
  52. return false;
  53. } else {
  54. if(!flipper_format_write_hex(
  55. file, UHF_RFU_BANK, UHF_BANK_DOES_NOT_EXIST, bank_data_length))
  56. return false;
  57. }
  58. // write epc data to file
  59. UHFData* epc_data = uhf_response_data_get_uhf_data(uhf_response_data, 2);
  60. if(epc_data->length) {
  61. if(!flipper_format_write_hex(
  62. file, UHF_EPC_BANK, epc_data->data + bank_data_start, bank_data_length))
  63. return false;
  64. } else {
  65. if(!flipper_format_write_hex(
  66. file, UHF_EPC_BANK, UHF_BANK_DOES_NOT_EXIST, bank_data_length))
  67. return false;
  68. }
  69. // write tid data to file
  70. UHFData* tid_data = uhf_response_data_get_uhf_data(uhf_response_data, 3);
  71. if(tid_data->length) {
  72. if(!flipper_format_write_hex(
  73. file, UHF_TID_BANK, tid_data->data + bank_data_start, bank_data_length))
  74. return false;
  75. } else {
  76. if(!flipper_format_write_hex(
  77. file, UHF_TID_BANK, UHF_BANK_DOES_NOT_EXIST, bank_data_length))
  78. return false;
  79. }
  80. // write user data to file
  81. UHFData* user_data = uhf_response_data_get_uhf_data(uhf_response_data, 4);
  82. if(user_data->length) {
  83. if(!flipper_format_write_hex(
  84. file, UHF_USER_BANK, user_data->data + bank_data_start, bank_data_length))
  85. return false;
  86. } else {
  87. if(!flipper_format_write_hex(
  88. file, UHF_USER_BANK, UHF_BANK_DOES_NOT_EXIST, bank_data_length))
  89. return false;
  90. }
  91. saved = true;
  92. } while(0);
  93. if(!saved) {
  94. dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile");
  95. }
  96. furi_string_free(temp_str);
  97. flipper_format_free(file);
  98. return saved;
  99. }
  100. bool uhf_device_save(UHFDevice* dev, const char* dev_name) {
  101. return uhf_device_save_file(
  102. dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, UHF_APP_EXTENSION, true);
  103. return false;
  104. }
  105. // uncomment
  106. static bool uhf_device_load_data(UHFDevice* dev, FuriString* path, bool show_dialog) {
  107. bool parsed = false;
  108. FlipperFormat* file = flipper_format_file_alloc(dev->storage);
  109. UHFResponseData* uhf_response_data = dev->dev_data;
  110. // reset response list
  111. uhf_response_data_reset(uhf_response_data);
  112. FuriString* temp_str;
  113. temp_str = furi_string_alloc();
  114. bool deprecated_version = false;
  115. if(dev->loading_cb) {
  116. dev->loading_cb(dev->loading_cb_ctx, true);
  117. }
  118. do {
  119. if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
  120. // Read and verify file header
  121. uint32_t version = 0;
  122. if(!flipper_format_read_header(file, temp_str, &version)) break;
  123. if(furi_string_cmp_str(temp_str, uhf_file_header) || (version != uhf_file_version)) {
  124. deprecated_version = true;
  125. break;
  126. }
  127. // Parse RFU Bank
  128. UHFData* rfu_bank = uhf_response_data_get_uhf_data(uhf_response_data, 0);
  129. if(!flipper_format_read_hex(file, UHF_RFU_BANK, rfu_bank->data, bank_data_length)) break;
  130. rfu_bank->length = bank_data_length;
  131. // Parse EPC Bank
  132. UHFData* epc_bank = uhf_response_data_add_new_uhf_data(uhf_response_data);
  133. if(!flipper_format_read_hex(file, UHF_EPC_BANK, epc_bank->data, bank_data_length)) break;
  134. epc_bank->length = bank_data_length;
  135. // Parse TID Bank
  136. UHFData* tid_bank = uhf_response_data_add_new_uhf_data(uhf_response_data);
  137. if(!flipper_format_read_hex(file, UHF_TID_BANK, tid_bank->data, bank_data_length)) break;
  138. tid_bank->length = bank_data_length;
  139. // Parse USER Bank
  140. UHFData* user_bank = uhf_response_data_add_new_uhf_data(uhf_response_data);
  141. if(!flipper_format_read_hex(file, UHF_USER_BANK, user_bank->data, bank_data_length)) break;
  142. user_bank->length = bank_data_length;
  143. parsed = true;
  144. } while(false);
  145. if(dev->loading_cb) {
  146. dev->loading_cb(dev->loading_cb_ctx, false);
  147. }
  148. if((!parsed) && (show_dialog)) {
  149. if(deprecated_version) {
  150. dialog_message_show_storage_error(dev->dialogs, "File format deprecated");
  151. } else {
  152. dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile");
  153. }
  154. }
  155. furi_string_free(temp_str);
  156. flipper_format_free(file);
  157. return parsed;
  158. }
  159. // void picopass_device_clear(UHFDevice* dev) {
  160. // furi_assert(dev);
  161. // picopass_device_data_clear(&dev->dev_data);
  162. // memset(&dev->dev_data, 0, sizeof(dev->dev_data));
  163. // dev->format = PicopassDeviceSaveFormatHF;
  164. // furi_string_reset(dev->load_path);
  165. // }
  166. void uhf_device_free(UHFDevice* uhf_dev) {
  167. furi_assert(uhf_dev);
  168. // picopass_device_clear(uhf_dev);
  169. furi_record_close(RECORD_STORAGE);
  170. furi_record_close(RECORD_DIALOGS);
  171. furi_string_free(uhf_dev->load_path);
  172. uhf_response_data_free(uhf_dev->dev_data);
  173. free(uhf_dev);
  174. }
  175. bool uhf_file_select(UHFDevice* dev) {
  176. furi_assert(dev);
  177. FuriString* uhf_app_folder;
  178. uhf_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
  179. DialogsFileBrowserOptions browser_options;
  180. dialog_file_browser_set_basic_options(&browser_options, UHF_APP_EXTENSION, &I_Nfc_10px);
  181. browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
  182. bool res =
  183. dialog_file_browser_show(dev->dialogs, dev->load_path, uhf_app_folder, &browser_options);
  184. furi_string_free(uhf_app_folder);
  185. if(res) {
  186. FuriString* filename;
  187. filename = furi_string_alloc();
  188. path_extract_filename(dev->load_path, filename, true);
  189. strncpy(dev->dev_name, furi_string_get_cstr(filename), UHF_DEV_NAME_MAX_LEN);
  190. res = uhf_device_load_data(dev, dev->load_path, true);
  191. if(res) {
  192. uhf_device_set_name(dev, dev->dev_name);
  193. }
  194. furi_string_free(filename);
  195. }
  196. return res;
  197. }
  198. // void uhf_device_data_clear(UHFDevice* dev_data) {
  199. // for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
  200. // memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data));
  201. // }
  202. // dev_data->pacs.legacy = false;
  203. // dev_data->pacs.se_enabled = false;
  204. // dev_data->pacs.elite_kdf = false;
  205. // dev_data->pacs.pin_length = 0;
  206. // }
  207. bool uhf_device_delete(UHFDevice* dev, bool use_load_path) {
  208. furi_assert(dev);
  209. bool deleted = false;
  210. FuriString* file_path;
  211. file_path = furi_string_alloc();
  212. do {
  213. // Delete original file
  214. if(use_load_path && !furi_string_empty(dev->load_path)) {
  215. furi_string_set(file_path, dev->load_path);
  216. } else {
  217. furi_string_printf(file_path, APP_DATA_PATH("%s%s"), dev->dev_name, UHF_APP_EXTENSION);
  218. }
  219. if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break;
  220. deleted = true;
  221. } while(0);
  222. if(!deleted) {
  223. dialog_message_show_storage_error(dev->dialogs, "Can not remove file");
  224. }
  225. furi_string_free(file_path);
  226. return deleted;
  227. }
  228. void uhf_device_set_loading_callback(UHFDevice* dev, UHFLoadingCallback callback, void* context) {
  229. furi_assert(dev);
  230. dev->loading_cb = callback;
  231. dev->loading_cb_ctx = context;
  232. }
  233. // ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) {
  234. // uint8_t key[32] = {0};
  235. // memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey));
  236. // mbedtls_des3_context ctx;
  237. // mbedtls_des3_init(&ctx);
  238. // mbedtls_des3_set2key_dec(&ctx, key);
  239. // mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data);
  240. // mbedtls_des3_free(&ctx);
  241. // return ERR_NONE;
  242. // }
  243. // ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) {
  244. // ReturnCode err;
  245. // pacs->biometrics = AA1[6].data[4];
  246. // pacs->pin_length = AA1[6].data[6] & 0x0F;
  247. // pacs->encryption = AA1[6].data[7];
  248. // if(pacs->encryption == PicopassDeviceEncryption3DES) {
  249. // FURI_LOG_D(TAG, "3DES Encrypted");
  250. // err = picopass_device_decrypt(AA1[7].data, pacs->credential);
  251. // if(err != ERR_NONE) {
  252. // FURI_LOG_E(TAG, "decrypt error %d", err);
  253. // return err;
  254. // }
  255. // err = picopass_device_decrypt(AA1[8].data, pacs->pin0);
  256. // if(err != ERR_NONE) {
  257. // FURI_LOG_E(TAG, "decrypt error %d", err);
  258. // return err;
  259. // }
  260. // err = picopass_device_decrypt(AA1[9].data, pacs->pin1);
  261. // if(err != ERR_NONE) {
  262. // FURI_LOG_E(TAG, "decrypt error %d", err);
  263. // return err;
  264. // }
  265. // } else if(pacs->encryption == PicopassDeviceEncryptionNone) {
  266. // FURI_LOG_D(TAG, "No Encryption");
  267. // memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN);
  268. // memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN);
  269. // memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN);
  270. // } else if(pacs->encryption == PicopassDeviceEncryptionDES) {
  271. // FURI_LOG_D(TAG, "DES Encrypted");
  272. // } else {
  273. // FURI_LOG_D(TAG, "Unknown encryption");
  274. // }
  275. // pacs->sio = (AA1[10].data[0] == 0x30); // rough check
  276. // return ERR_NONE;
  277. // }
  278. // ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) {
  279. // uint32_t* halves = (uint32_t*)data;
  280. // if(halves[0] == 0) {
  281. // uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
  282. // record->bitLength = 31 - leading0s;
  283. // } else {
  284. // uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));
  285. // record->bitLength = 63 - leading0s;
  286. // }
  287. // FURI_LOG_D(TAG, "bitLength: %d", record->bitLength);
  288. // if(record->bitLength == 26) {
  289. // uint8_t* v4 = data + 4;
  290. // uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24);
  291. // record->CardNumber = (bot >> 1) & 0xFFFF;
  292. // record->FacilityCode = (bot >> 17) & 0xFF;
  293. // FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber);
  294. // record->valid = true;
  295. // } else {
  296. // record->CardNumber = 0;
  297. // record->FacilityCode = 0;
  298. // record->valid = false;
  299. // }
  300. // return ERR_NONE;
  301. // }