troika.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #include <flipper_application.h>
  2. #include "../../metroflip_i.h"
  3. #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
  4. #include <nfc/protocols/mf_classic/mf_classic.h>
  5. #include <nfc/protocols/mf_classic/mf_classic_poller.h>
  6. #include "../../api/mosgortrans/mosgortrans_util.h"
  7. #include <dolphin/dolphin.h>
  8. #include <bit_lib.h>
  9. #include <furi_hal.h>
  10. #include <nfc/nfc.h>
  11. #include <nfc/nfc_device.h>
  12. #include <nfc/nfc_listener.h>
  13. #include "../../api/metroflip/metroflip_api.h"
  14. #include "../../metroflip_plugins.h"
  15. #define TAG "Metroflip:Scene:Troika"
  16. const MfClassicKeyPair troika_1k_keys[16] = {
  17. {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58},
  18. {.a = 0xa82607b01c0d, .b = 0x2910989b6880},
  19. {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
  20. {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
  21. {.a = 0x73068f118c13, .b = 0x2b7f3253fac5},
  22. {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698},
  23. {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
  24. {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dba},
  25. {.a = 0xa73f5dc1d333, .b = 0xe35173494a81},
  26. {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763},
  27. {.a = 0x9becdf3d9273, .b = 0xf8493407799d},
  28. {.a = 0x08b386463229, .b = 0x5efbaecef46b},
  29. {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0},
  30. {.a = 0xa82607b01c0d, .b = 0x2910989b6880},
  31. {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75},
  32. {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
  33. };
  34. const MfClassicKeyPair troika_4k_keys[40] = {
  35. {.a = 0xEC29806D9738, .b = 0xFBF225DC5D58}, //1
  36. {.a = 0xA0A1A2A3A4A5, .b = 0x7DE02A7F6025}, //2
  37. {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //3
  38. {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //4
  39. {.a = 0x73068F118C13, .b = 0x2B7F3253FAC5}, //5
  40. {.a = 0xFBC2793D540B, .b = 0xD3A297DC2698}, //6
  41. {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //7
  42. {.a = 0xAE3D65A3DAD4, .b = 0x0F1C63013DBA}, //8
  43. {.a = 0xA73F5DC1D333, .b = 0xE35173494A81}, //9
  44. {.a = 0x69A32F1C2F19, .b = 0x6B8BD9860763}, //10
  45. {.a = 0x9BECDF3D9273, .b = 0xF8493407799D}, //11
  46. {.a = 0x08B386463229, .b = 0x5EFBAECEF46B}, //12
  47. {.a = 0xCD4C61C26E3D, .b = 0x31C7610DE3B0}, //13
  48. {.a = 0xA82607B01C0D, .b = 0x2910989B6880}, //14
  49. {.a = 0x0E8F64340BA4, .b = 0x4ACEC1205D75}, //15
  50. {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //16
  51. {.a = 0x6B02733BB6EC, .b = 0x7038CD25C408}, //17
  52. {.a = 0x403D706BA880, .b = 0xB39D19A280DF}, //18
  53. {.a = 0xC11F4597EFB5, .b = 0x70D901648CB9}, //19
  54. {.a = 0x0DB520C78C1C, .b = 0x73E5B9D9D3A4}, //20
  55. {.a = 0x3EBCE0925B2F, .b = 0x372CC880F216}, //21
  56. {.a = 0x16A27AF45407, .b = 0x9868925175BA}, //22
  57. {.a = 0xABA208516740, .b = 0xCE26ECB95252}, //23
  58. {.a = 0xCD64E567ABCD, .b = 0x8F79C4FD8A01}, //24
  59. {.a = 0x764CD061F1E6, .b = 0xA74332F74994}, //25
  60. {.a = 0x1CC219E9FEC1, .b = 0xB90DE525CEB6}, //26
  61. {.a = 0x2FE3CB83EA43, .b = 0xFBA88F109B32}, //27
  62. {.a = 0x07894FFEC1D6, .b = 0xEFCB0E689DB3}, //28
  63. {.a = 0x04C297B91308, .b = 0xC8454C154CB5}, //29
  64. {.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30
  65. {.a = 0x7545DF809202, .b = 0xECF751084A80}, //31
  66. {.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32
  67. {.a = 0x7A86AA203788, .b = 0xE41242278CA2}, //33
  68. {.a = 0xAFCEF64C9913, .b = 0x9DB96DCA4324}, //34
  69. {.a = 0x04EAA462F70B, .b = 0xAC17B93E2FAE}, //35
  70. {.a = 0xE734C210F27E, .b = 0x29BA8C3E9FDA}, //36
  71. {.a = 0xD5524F591EED, .b = 0x5DAF42861B4D}, //37
  72. {.a = 0xE4821A377B75, .b = 0xE8709E486465}, //38
  73. {.a = 0x518DC6EEA089, .b = 0x97C64AC98CA4}, //39
  74. {.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40
  75. };
  76. static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {
  77. bool success = true;
  78. if(type == MfClassicType1k) {
  79. config->data_sector = 11;
  80. config->keys = troika_1k_keys;
  81. } else if(type == MfClassicType4k) {
  82. config->data_sector = 8; // Further testing needed
  83. config->keys = troika_4k_keys;
  84. } else {
  85. success = false;
  86. }
  87. return success;
  88. }
  89. static bool troika_parse(FuriString* parsed_data, const MfClassicData* data) {
  90. bool parsed = false;
  91. do {
  92. // Verify card type
  93. TroikaCardConfig cfg = {};
  94. if(!troika_get_card_config(&cfg, data->type)) break;
  95. // Verify key
  96. const MfClassicSectorTrailer* sec_tr =
  97. mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector);
  98. const uint64_t key =
  99. bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
  100. if(key != cfg.keys[cfg.data_sector].a) break;
  101. FuriString* metro_result = furi_string_alloc();
  102. FuriString* ground_result = furi_string_alloc();
  103. FuriString* tat_result = furi_string_alloc();
  104. bool is_metro_data_present =
  105. mosgortrans_parse_transport_block(&data->block[32], metro_result);
  106. bool is_ground_data_present =
  107. mosgortrans_parse_transport_block(&data->block[28], ground_result);
  108. bool is_tat_data_present = mosgortrans_parse_transport_block(&data->block[16], tat_result);
  109. furi_string_cat_printf(parsed_data, "\e#Troyka card\n");
  110. if(is_metro_data_present && !furi_string_empty(metro_result)) {
  111. render_section_header(parsed_data, "Metro", 22, 21);
  112. furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(metro_result));
  113. }
  114. if(is_ground_data_present && !furi_string_empty(ground_result)) {
  115. render_section_header(parsed_data, "Ediny", 22, 22);
  116. furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(ground_result));
  117. }
  118. if(is_tat_data_present && !furi_string_empty(tat_result)) {
  119. render_section_header(parsed_data, "TAT", 24, 23);
  120. furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(tat_result));
  121. }
  122. furi_string_free(tat_result);
  123. furi_string_free(ground_result);
  124. furi_string_free(metro_result);
  125. parsed = is_metro_data_present || is_ground_data_present || is_tat_data_present;
  126. } while(false);
  127. return parsed;
  128. }
  129. bool checked = false;
  130. static NfcCommand troika_poller_callback(NfcGenericEvent event, void* context) {
  131. furi_assert(context);
  132. furi_assert(event.event_data);
  133. furi_assert(event.protocol == NfcProtocolMfClassic);
  134. NfcCommand command = NfcCommandContinue;
  135. const MfClassicPollerEvent* mfc_event = event.event_data;
  136. Metroflip* app = context;
  137. if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {
  138. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventCardDetected);
  139. command = NfcCommandContinue;
  140. } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
  141. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventCardLost);
  142. app->sec_num = 0;
  143. command = NfcCommandStop;
  144. } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
  145. mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;
  146. } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {
  147. MfClassicKey key = {0};
  148. MfClassicKeyType key_type = MfClassicKeyTypeA;
  149. bit_lib_num_to_bytes_be(troika_1k_keys[app->sec_num].a, COUNT_OF(key.data), key.data);
  150. if(!checked) {
  151. mfc_event->data->read_sector_request_data.sector_num = app->sec_num;
  152. mfc_event->data->read_sector_request_data.key = key;
  153. mfc_event->data->read_sector_request_data.key_type = key_type;
  154. mfc_event->data->read_sector_request_data.key_provided = true;
  155. app->sec_num++;
  156. checked = true;
  157. }
  158. nfc_device_set_data(
  159. app->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(app->poller));
  160. const MfClassicData* mfc_data = nfc_device_get_data(app->nfc_device, NfcProtocolMfClassic);
  161. if(mfc_data->type == MfClassicType1k) {
  162. bit_lib_num_to_bytes_be(troika_1k_keys[app->sec_num].a, COUNT_OF(key.data), key.data);
  163. mfc_event->data->read_sector_request_data.sector_num = app->sec_num;
  164. mfc_event->data->read_sector_request_data.key = key;
  165. mfc_event->data->read_sector_request_data.key_type = key_type;
  166. mfc_event->data->read_sector_request_data.key_provided = true;
  167. if(app->sec_num == 16) {
  168. mfc_event->data->read_sector_request_data.key_provided = false;
  169. app->sec_num = 0;
  170. }
  171. app->sec_num++;
  172. } else if(mfc_data->type == MfClassicType4k) {
  173. bit_lib_num_to_bytes_be(troika_4k_keys[app->sec_num].a, COUNT_OF(key.data), key.data);
  174. mfc_event->data->read_sector_request_data.sector_num = app->sec_num;
  175. mfc_event->data->read_sector_request_data.key = key;
  176. mfc_event->data->read_sector_request_data.key_type = key_type;
  177. mfc_event->data->read_sector_request_data.key_provided = true;
  178. if(app->sec_num == 40) {
  179. mfc_event->data->read_sector_request_data.key_provided = false;
  180. app->sec_num = 0;
  181. }
  182. app->sec_num++;
  183. }
  184. } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
  185. const MfClassicData* mfc_data = nfc_device_get_data(app->nfc_device, NfcProtocolMfClassic);
  186. FuriString* parsed_data = furi_string_alloc();
  187. Widget* widget = app->widget;
  188. if(!troika_parse(parsed_data, mfc_data)) {
  189. furi_string_reset(app->text_box_store);
  190. FURI_LOG_I(TAG, "Unknown card type");
  191. furi_string_printf(parsed_data, "\e#Unknown card\n");
  192. }
  193. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  194. widget_add_button_element(
  195. widget, GuiButtonTypeRight, "Exit", metroflip_exit_widget_callback, app);
  196. furi_string_free(parsed_data);
  197. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  198. metroflip_app_blink_stop(app);
  199. command = NfcCommandStop;
  200. } else if(mfc_event->type == MfClassicPollerEventTypeFail) {
  201. FURI_LOG_I(TAG, "fail");
  202. command = NfcCommandStop;
  203. }
  204. return command;
  205. }
  206. static void troika_on_enter(Metroflip* app) {
  207. dolphin_deed(DolphinDeedNfcRead);
  208. app->sec_num = 0;
  209. // Setup view
  210. Popup* popup = app->popup;
  211. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  212. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  213. // Start worker
  214. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  215. nfc_scanner_alloc(app->nfc);
  216. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolMfClassic);
  217. nfc_poller_start(app->poller, troika_poller_callback, app);
  218. metroflip_app_blink_start(app);
  219. }
  220. static bool troika_on_event(Metroflip* app, SceneManagerEvent event) {
  221. bool consumed = false;
  222. if(event.type == SceneManagerEventTypeCustom) {
  223. if(event.event == MetroflipCustomEventCardDetected) {
  224. Popup* popup = app->popup;
  225. popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
  226. consumed = true;
  227. } else if(event.event == MetroflipCustomEventCardLost) {
  228. Popup* popup = app->popup;
  229. popup_set_header(popup, "Card \n lost", 68, 30, AlignLeft, AlignTop);
  230. consumed = true;
  231. } else if(event.event == MetroflipCustomEventWrongCard) {
  232. Popup* popup = app->popup;
  233. popup_set_header(popup, "WRONG \n CARD", 68, 30, AlignLeft, AlignTop);
  234. consumed = true;
  235. } else if(event.event == MetroflipCustomEventPollerFail) {
  236. Popup* popup = app->popup;
  237. popup_set_header(popup, "Failed", 68, 30, AlignLeft, AlignTop);
  238. consumed = true;
  239. }
  240. } else if(event.type == SceneManagerEventTypeBack) {
  241. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  242. consumed = true;
  243. }
  244. return consumed;
  245. }
  246. static void troika_on_exit(Metroflip* app) {
  247. widget_reset(app->widget);
  248. if(app->poller) {
  249. nfc_poller_stop(app->poller);
  250. nfc_poller_free(app->poller);
  251. }
  252. // Clear view
  253. popup_reset(app->popup);
  254. metroflip_app_blink_stop(app);
  255. }
  256. /* Actual implementation of app<>plugin interface */
  257. static const MetroflipPlugin troika_plugin = {
  258. .card_name = "Troika",
  259. .plugin_on_enter = troika_on_enter,
  260. .plugin_on_event = troika_on_event,
  261. .plugin_on_exit = troika_on_exit,
  262. };
  263. /* Plugin descriptor to comply with basic plugin specification */
  264. static const FlipperAppPluginDescriptor troika_plugin_descriptor = {
  265. .appid = METROFLIP_SUPPORTED_CARD_PLUGIN_APP_ID,
  266. .ep_api_version = METROFLIP_SUPPORTED_CARD_PLUGIN_API_VERSION,
  267. .entry_point = &troika_plugin,
  268. };
  269. /* Plugin entry point - must return a pointer to const descriptor */
  270. const FlipperAppPluginDescriptor* troika_plugin_ep(void) {
  271. return &troika_plugin_descriptor;
  272. }