metroflip_scene_metromoney.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. * Parser for Metromoney card (Georgia).
  3. *
  4. * Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <flipper_application.h>
  20. #include "../metroflip_i.h"
  21. #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
  22. #include <nfc/protocols/mf_classic/mf_classic.h>
  23. #include <nfc/protocols/mf_classic/mf_classic_poller.h>
  24. #include <dolphin/dolphin.h>
  25. #include <bit_lib.h>
  26. #include <furi_hal.h>
  27. #include <nfc/nfc.h>
  28. #include <nfc/nfc_device.h>
  29. #include <nfc/nfc_listener.h>
  30. #define TAG "Metroflip:Scene:Metromoney"
  31. typedef struct {
  32. uint64_t a;
  33. uint64_t b;
  34. } MfClassicKeyPair;
  35. static const MfClassicKeyPair metromoney_1k_keys[] = {
  36. {.a = 0x2803BCB0C7E1, .b = 0x4FA9EB49F75E},
  37. {.a = 0x9C616585E26D, .b = 0xD1C71E590D16},
  38. {.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
  39. {.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
  40. {.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
  41. {.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
  42. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  43. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  44. {.a = 0x112233445566, .b = 0x361A62F35BC9},
  45. {.a = 0x112233445566, .b = 0x361A62F35BC9},
  46. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  47. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  48. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  49. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  50. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  51. {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
  52. };
  53. static bool metromoney_parse(const NfcDevice* device, const MfClassicData* data, Metroflip* app) {
  54. furi_assert(device);
  55. bool parsed = false;
  56. do {
  57. // Verify key
  58. const uint8_t ticket_sector_number = 1;
  59. const uint8_t ticket_block_number = 1;
  60. const MfClassicSectorTrailer* sec_tr =
  61. mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);
  62. const uint64_t key =
  63. bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
  64. if(key != metromoney_1k_keys[ticket_sector_number].a) break;
  65. // Parse data
  66. const uint8_t start_block_num =
  67. mf_classic_get_first_block_num_of_sector(ticket_sector_number);
  68. const uint8_t* block_start_ptr =
  69. &data->block[start_block_num + ticket_block_number].data[0];
  70. uint32_t balance = bit_lib_bytes_to_num_le(block_start_ptr, 4) - 100;
  71. uint32_t balance_lari = balance / 100;
  72. uint8_t balance_tetri = balance % 100;
  73. size_t uid_len = 0;
  74. const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
  75. uint32_t card_number = bit_lib_bytes_to_num_le(uid, 4);
  76. strncpy(app->card_type, "Metromoney", sizeof(app->card_type));
  77. app->balance_lari = balance_lari;
  78. app->balance_tetri = balance_tetri;
  79. app->card_number = card_number;
  80. parsed = true;
  81. } while(false);
  82. return parsed;
  83. }
  84. static NfcCommand
  85. metroflip_scene_metromoney_poller_callback(NfcGenericEvent event, void* context) {
  86. furi_assert(context);
  87. furi_assert(event.event_data);
  88. furi_assert(event.protocol == NfcProtocolMfClassic);
  89. NfcCommand command = NfcCommandContinue;
  90. const MfClassicPollerEvent* mfc_event = event.event_data;
  91. Metroflip* app = context;
  92. if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {
  93. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventCardDetected);
  94. command = NfcCommandContinue;
  95. } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
  96. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventCardLost);
  97. app->sec_num = 0;
  98. command = NfcCommandStop;
  99. } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
  100. mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;
  101. } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {
  102. MfClassicKey key = {0};
  103. bit_lib_num_to_bytes_be(metromoney_1k_keys[app->sec_num].a, COUNT_OF(key.data), key.data);
  104. MfClassicKeyType key_type = MfClassicKeyTypeA;
  105. mfc_event->data->read_sector_request_data.sector_num = app->sec_num;
  106. mfc_event->data->read_sector_request_data.key = key;
  107. mfc_event->data->read_sector_request_data.key_type = key_type;
  108. mfc_event->data->read_sector_request_data.key_provided = true;
  109. if(app->sec_num == 16) {
  110. mfc_event->data->read_sector_request_data.key_provided = false;
  111. app->sec_num = 0;
  112. }
  113. app->sec_num++;
  114. } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
  115. nfc_device_set_data(
  116. app->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(app->poller));
  117. const MfClassicData* mfc_data = nfc_device_get_data(app->nfc_device, NfcProtocolMfClassic);
  118. metromoney_parse(app->nfc_device, mfc_data, app);
  119. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerSuccess);
  120. command = NfcCommandStop;
  121. metroflip_app_blink_stop(app);
  122. } else if(mfc_event->type == MfClassicPollerEventTypeFail) {
  123. FURI_LOG_I(TAG, "fail");
  124. command = NfcCommandStop;
  125. }
  126. return command;
  127. }
  128. void metroflip_scene_metromoney_on_enter(void* context) {
  129. Metroflip* app = context;
  130. dolphin_deed(DolphinDeedNfcRead);
  131. app->sec_num = 0;
  132. // Setup view
  133. Popup* popup = app->popup;
  134. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  135. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  136. // Start worker
  137. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  138. nfc_scanner_alloc(app->nfc);
  139. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolMfClassic);
  140. nfc_poller_start(app->poller, metroflip_scene_metromoney_poller_callback, app);
  141. metroflip_app_blink_start(app);
  142. }
  143. bool metroflip_scene_metromoney_on_event(void* context, SceneManagerEvent event) {
  144. Metroflip* app = context;
  145. bool consumed = false;
  146. if(event.type == SceneManagerEventTypeCustom) {
  147. if(event.event == MetroflipCustomEventCardDetected) {
  148. Popup* popup = app->popup;
  149. popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
  150. consumed = true;
  151. } else if(event.event == MetroflipCustomEventCardLost) {
  152. Popup* popup = app->popup;
  153. popup_set_header(popup, "Card \n lost", 68, 30, AlignLeft, AlignTop);
  154. consumed = true;
  155. } else if(event.event == MetroflipCustomEventWrongCard) {
  156. Popup* popup = app->popup;
  157. popup_set_header(popup, "WRONG \n CARD", 68, 30, AlignLeft, AlignTop);
  158. consumed = true;
  159. } else if(event.event == MetroflipCustomEventPollerFail) {
  160. Popup* popup = app->popup;
  161. popup_set_header(popup, "Failed", 68, 30, AlignLeft, AlignTop);
  162. consumed = true;
  163. } else if(event.event == MetroflipCustomEventPollerSuccess) {
  164. scene_manager_next_scene(app->scene_manager, MetroflipSceneReadSuccess);
  165. consumed = true;
  166. }
  167. } else if(event.type == SceneManagerEventTypeBack) {
  168. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  169. consumed = true;
  170. }
  171. return consumed;
  172. }
  173. void metroflip_scene_metromoney_on_exit(void* context) {
  174. Metroflip* app = context;
  175. widget_reset(app->widget);
  176. if(app->poller) {
  177. nfc_poller_stop(app->poller);
  178. nfc_poller_free(app->poller);
  179. }
  180. // Clear view
  181. popup_reset(app->popup);
  182. metroflip_app_blink_stop(app);
  183. }